Index: Jenkinsfile
===================================================================
--- Jenkinsfile	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ Jenkinsfile	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -127,5 +127,7 @@
 			}
 
-			sh "${SrcDir}/configure CXX=${Settings.Compiler.CXX} CC=${Settings.Compiler.CC} ${Settings.Architecture.flags} AR=gcc-ar RANLIB=gcc-ranlib ${targets} --quiet --prefix=${BuildDir}"
+			ast = Settings.NewAST ? "--enable-new-ast" : "--disable-new-ast"
+
+			sh "${SrcDir}/configure CXX=${Settings.Compiler.CXX} CC=${Settings.Compiler.CC} ${Settings.Architecture.flags} AR=gcc-ar RANLIB=gcc-ranlib ${targets} ${ast} --quiet --prefix=${BuildDir}"
 
 			// Configure libcfa
@@ -359,4 +361,5 @@
 	public final CC_Desc Compiler
 	public final Arch_Desc Architecture
+	public final Boolean NewAST
 	public final Boolean RunAllTests
 	public final Boolean RunBenchmark
@@ -410,4 +413,5 @@
 
 		this.IsSandbox          = (branch == "jenkins-sandbox")
+		this.NewAST             = param.NewAST
 		this.RunAllTests        = param.RunAllTests
 		this.RunBenchmark       = param.RunBenchmark
@@ -470,8 +474,13 @@
 				],												\
 				[$class: 'BooleanParameterDefinition',  						\
+					description: 'If true, build compiler using new AST', 		\
+					name: 'NewAST', 									\
+					defaultValue: false,  								\
+				], 												\
+				[$class: 'BooleanParameterDefinition',  						\
 					description: 'If false, only the quick test suite is ran', 		\
 					name: 'RunAllTests', 								\
 					defaultValue: false,  								\
-				], 												\
+				],
 				[$class: 'BooleanParameterDefinition',  						\
 					description: 'If true, jenkins also runs benchmarks', 		\
Index: benchmark/readyQ/cycle.cfa
===================================================================
--- benchmark/readyQ/cycle.cfa	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
+++ benchmark/readyQ/cycle.cfa	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -0,0 +1,79 @@
+#include "rq_bench.hfa"
+
+thread Partner {
+	Partner * partner;
+	unsigned long long count;
+};
+
+void ?{}( Partner & this ) {
+	((thread&)this){ bench_cluster };
+}
+
+void main( Partner & this ) {
+	this.count = 0;
+	for() {
+		park();
+		unpark( *this.partner );
+		this.count ++;
+		if( clock_mode && stop) break;
+		if(!clock_mode && this.count >= stop_count) break;
+	}
+
+	__atomic_fetch_add(&threads_left, -1, __ATOMIC_SEQ_CST);
+}
+
+int main(int argc, char * argv[]) {
+	unsigned ring_size = 2;
+	cfa_option opt[] = {
+		BENCH_OPT,
+		{ 'r', "ringsize", "Number of threads in a cycle", ring_size }
+	};
+	BENCH_OPT_PARSE("cforall cycle benchmark");
+
+	{
+		unsigned long long global_counter = 0;
+		unsigned tthreads = nthreads * ring_size;
+		Time start, end;
+		BenchCluster bc = { nprocs };
+		{
+			threads_left = tthreads;
+			Partner threads[tthreads];
+			for(i; tthreads) {
+				unsigned pi = (i + nthreads) % tthreads;
+				threads[i].partner = &threads[pi];
+			}
+			printf("Starting\n");
+
+			bool is_tty = isatty(STDOUT_FILENO);
+			start = getTimeNsec();
+
+			for(i; nthreads) {
+				unpark( threads[i] );
+			}
+			wait(start, is_tty);
+
+			stop = true;
+			end = getTimeNsec();
+			printf("\nDone\n");
+
+			for(i; tthreads) {
+				global_counter += join( threads[i] ).count;
+			}
+		}
+
+		printf("Duration (ms)       : %'ld\n", (end - start)`ms);
+		printf("Number of processors: %'d\n", nprocs);
+		printf("Number of threads   : %'d\n", tthreads);
+		printf("Cycle size (# thrds): %'d\n", ring_size);
+		printf("Yields per second   : %'18.2lf\n", ((double)global_counter) / (end - start)`s);
+		printf("ns per yields       : %'18.2lf\n", ((double)(end - start)`ns) / global_counter);
+		printf("Total yields        : %'15llu\n", global_counter);
+		printf("Yields per threads  : %'15llu\n", global_counter / tthreads);
+		printf("Yields per procs    : %'15llu\n", global_counter / nprocs);
+		printf("Yields/sec/procs    : %'18.2lf\n", (((double)global_counter) / nprocs) / (end - start)`s);
+		printf("ns per yields/procs : %'18.2lf\n", ((double)(end - start)`ns) / (global_counter / nprocs));
+		fflush(stdout);
+	}
+
+	return 0;
+}
Index: benchmark/readyQ/cycle.go
===================================================================
--- benchmark/readyQ/cycle.go	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
+++ benchmark/readyQ/cycle.go	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -0,0 +1,140 @@
+package main
+
+import (
+	"bufio"
+	"flag"
+	"fmt"
+	"os"
+	"runtime"
+	"sync/atomic"
+	"time"
+	"golang.org/x/text/language"
+	"golang.org/x/text/message"
+)
+
+var clock_mode bool
+var threads_left int64
+var stop int32
+var duration float64
+var stop_count uint64
+
+func fflush(f *bufio.Writer) {
+	defer f.Flush()
+	f.Write([]byte("\r"))
+}
+
+func wait(start time.Time, is_tty bool) {
+	f := bufio.NewWriter(os.Stdout)
+	tdur := time.Duration(duration)
+	for true {
+		time.Sleep(100 * time.Millisecond)
+		end := time.Now()
+		delta := end.Sub(start)
+		if is_tty {
+			fmt.Printf(" %.1f",delta.Seconds())
+			fflush(f)
+		}
+		if clock_mode && delta >= (tdur * time.Second) {
+			break
+		} else if !clock_mode && atomic.LoadInt64(&threads_left) == 0 {
+			break
+		}
+	}
+}
+
+func partner(result chan uint64, mine chan int, next chan int) {
+	count := uint64(0)
+	for true {
+		<- mine
+		next <- 0
+		count += 1
+		if  clock_mode && atomic.LoadInt32(&stop) == 1 { break }
+		if !clock_mode && count >= stop_count { break }
+	}
+
+	atomic.AddInt64(&threads_left, -1);
+	result <- count
+}
+
+func main() {
+	var nprocs int
+	var nthreads int
+	var ring_size int
+
+	nprocsOpt := flag.Int("p", 1, "The number of processors")
+	nthreadsOpt := flag.Int("t", 1, "The number of threads")
+	ring_sizeOpt := flag.Int("r", 2, "The number of threads per cycles")
+	durationOpt := flag.Float64("d", 0, "Duration of the experiment in seconds")
+	stopOpt := flag.Uint64("i", 0, "Duration of the experiment in iterations")
+
+	flag.Parse()
+
+	nprocs = *nprocsOpt
+	nthreads = *nthreadsOpt
+	ring_size = *ring_sizeOpt
+	duration = *durationOpt
+	stop_count = *stopOpt
+
+	if duration > 0 && stop_count > 0 {
+		panic(fmt.Sprintf("--duration and --iterations cannot be used together\n"))
+	} else if duration > 0 {
+		clock_mode = true
+		stop_count = 0xFFFFFFFFFFFFFFFF
+		fmt.Printf("Running for %f seconds\n", duration)
+	} else if stop_count > 0 {
+		clock_mode = false
+		fmt.Printf("Running for %d iterations\n", stop_count)
+	} else {
+		duration = 5
+		clock_mode = true
+		fmt.Printf("Running for %f seconds\n", duration)
+	}
+
+	runtime.GOMAXPROCS(nprocs)
+	tthreads := nthreads * ring_size
+	threads_left = int64(tthreads)
+
+	result := make(chan uint64)
+	channels := make([]chan int, tthreads)
+	for i := range channels {
+		channels[i] = make(chan int, 1)
+	}
+
+	for i := 0; i < tthreads; i++ {
+		pi := (i + nthreads) % tthreads
+		go partner(result, channels[i], channels[pi])
+	}
+	fmt.Printf("Starting\n");
+
+	atomic.StoreInt32(&stop, 0)
+	start := time.Now()
+	for i := 0; i < nthreads; i++ {
+		channels[i] <- 0
+	}
+	wait(start, true);
+
+	atomic.StoreInt32(&stop, 1)
+	end := time.Now()
+	delta := end.Sub(start)
+
+	fmt.Printf("\nDone\n")
+
+	global_counter := uint64(0)
+	for i := 0; i < tthreads; i++ {
+		global_counter += <- result
+	}
+
+	p := message.NewPrinter(language.English)
+	p.Printf("Duration (ms)       : %f\n", delta.Seconds());
+	p.Printf("Number of processors: %d\n", nprocs);
+	p.Printf("Number of threads   : %d\n", tthreads);
+	p.Printf("Cycle size (# thrds): %d\n", ring_size);
+	p.Printf("Yields per second   : %18.2f\n", float64(global_counter) / delta.Seconds())
+	p.Printf("ns per yields       : %18.2f\n", float64(delta.Nanoseconds()) / float64(global_counter))
+	p.Printf("Total yields        : %15d\n", global_counter)
+	p.Printf("Yields per threads  : %15d\n", global_counter / uint64(tthreads))
+	p.Printf("Yields per procs    : %15d\n", global_counter / uint64(nprocs))
+	p.Printf("Yields/sec/procs    : %18.2f\n", (float64(global_counter) / float64(nprocs)) / delta.Seconds())
+	p.Printf("ns per yields/procs : %18.2f\n", float64(delta.Nanoseconds()) / (float64(global_counter) / float64(nprocs)))
+
+}
Index: benchmark/readyQ/rq_bench.hfa
===================================================================
--- benchmark/readyQ/rq_bench.hfa	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
+++ benchmark/readyQ/rq_bench.hfa	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -0,0 +1,88 @@
+#include <clock.hfa>
+#include <kernel.hfa>
+#include <parseargs.hfa>
+#include <stdio.h>
+#include <stdlib.hfa>
+#include <thread.hfa>
+#include <time.hfa>
+#include <unistd.h>
+
+volatile bool stop = false;
+bool clock_mode;
+double duration = -1;
+unsigned long long stop_count = 0;
+unsigned nprocs = 1;
+unsigned nthreads = 1;
+
+volatile unsigned long long threads_left;
+
+#define thread_loop for(this.count = 0;; this.count++)
+
+#define BENCH_OPT \
+	{'d', "duration",  "Duration of the experiments in seconds", duration }, \
+	{'i', "iterations",  "Number of iterations of the experiments", stop_count }, \
+	{'t', "nthreads",  "Number of threads to use", nthreads }, \
+	{'p', "nprocs",    "Number of processors to use", nprocs }
+
+#define BENCH_OPT_PARSE(name) \
+	{ \
+		int opt_cnt = sizeof(opt) / sizeof(cfa_option); \
+		char **left; \
+		parse_args( argc, argv, opt, opt_cnt, "[OPTIONS]...\n" name, left ); \
+		if(duration > 0 && stop_count > 0) { \
+			fprintf(stderr, "--duration and --iterations cannot be used together\n"); \
+			print_args_usage(argc, argv, opt, opt_cnt, "[OPTIONS]...\n" name, true); \
+		} else if(duration > 0) { \
+			clock_mode = true; \
+			stop_count = 0xFFFFFFFFFFFFFFFF; \
+			printf("Running for %lf seconds\n", duration); \
+		} else if(stop_count > 0) { \
+			clock_mode = false; \
+			printf("Running for %lu iterations\n", stop_count); \
+		} else { \
+			duration = 5; clock_mode = true;\
+			printf("Running for %lf seconds\n", duration); \
+		} \
+	}
+
+struct cluster & bench_cluster;
+
+struct BenchCluster {
+	cluster cl;
+	processor * procs;
+	unsigned nprocs;
+};
+
+void ?{}( BenchCluster & this, unsigned nprocs ) {
+	(this.cl){ "Benchmark Cluster" };
+	&bench_cluster = &this.cl;
+	this.nprocs = nprocs;
+	this.procs  = alloc( this.nprocs );
+	for(i; this.nprocs){
+		processor * p = &this.procs[i];
+		(*p){ "Benchmark Processor", this.cl };
+	}
+}
+
+void ^?{}( BenchCluster & this ) {
+	adelete( this.nprocs, this.procs );
+	^(this.cl){};
+}
+
+void wait(const Time & start, bool is_tty) {
+	for() {
+		sleep(100`ms);
+		Time end = getTimeNsec();
+		Duration delta = end - start;
+		if(is_tty) {
+			printf(" %.1f\r", delta`ds);
+			fflush(stdout);
+		}
+		if( clock_mode && delta >= duration`s ) {
+			break;
+		}
+		else if( !clock_mode && threads_left == 0 ) {
+			break;
+		}
+	}
+}
Index: benchmark/rmit.py
===================================================================
--- benchmark/rmit.py	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
+++ benchmark/rmit.py	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -0,0 +1,219 @@
+#!/usr/bin/python3
+"""
+Python Script to implement R.M.I.T. testing : Randomized Multiple Interleaved Trials
+
+./rmit.py run COMMAND CANDIDATES
+-t trials
+-o option:values
+"""
+
+
+import argparse
+import itertools
+import os
+import random
+import re
+import subprocess
+import sys
+
+
+def parse_range(x):
+    result = []
+    for part in x.split(','):
+        if '-' in part:
+            a, b = part.split('-')
+            a, b = int(a), int(b)
+            result.extend(range(a, b + 1))
+        else:
+            a = int(part)
+            result.append(a)
+    return result
+
+class DependentOpt:
+	def __init__(self, key, value):
+		self.key = key
+		self.value = value
+		self.vars = re.findall("[a-zA-Z]", value)
+
+def parse_option(opt):
+	match = re.search("^(.*):(.*)$", opt)
+	if not match :
+		print('ERROR: extra options should match pattern .*:.*, got {}'.format(opt), file=sys.stderr)
+		sys.exit(1)
+
+	key    = match.group(1)
+	values = match.group(2)
+
+	try:
+		num = int(values)
+		return key, [num]
+	except:
+		pass
+
+	if re.search("^[0-9-,]+$", values):
+		values = parse_range(values)
+		return key, [v for v in values]
+	else:
+		return key, DependentOpt(key, values)
+
+def eval_one(fmt, vals):
+	orig = fmt
+	for k, v in vals:
+		fmt = fmt.replace(k, str(v))
+
+	if not re.search("^[0-9-/*+ ]+$", fmt):
+		print('ERROR: pattern option {} (interpreted as {}) could not be evaluated'.format(orig, fmt), file=sys.stderr)
+		sys.exit(1)
+
+	return eval(fmt)
+
+def eval_options(opts):
+	dependents = [d for d in opts.values() if type(d) is DependentOpt]
+	processed = []
+	nopts = []
+	for d in dependents:
+		processed.append(d.key)
+		lists = []
+		for dvar in d.vars:
+			if not dvar in opts.keys():
+				print('ERROR: extra pattern option {}:{} uses unknown key {}'.format(d.key,d.value,dvar), file=sys.stderr)
+				sys.exit(1)
+
+			lists.append([(dvar, o) for o in opts[dvar]])
+			processed.append(dvar)
+
+		kopt = []
+		for vals in list(itertools.product(*lists)):
+			res = ['-{}'.format(d.key), "{}".format(eval_one(d.value, vals))]
+			for k, v in vals:
+				res.extend(['-{}'.format(k), "{}".format(v)])
+			kopt.append(res)
+		nopts.append(kopt)
+
+
+	for k, vals in opts.items():
+		if k not in processed:
+			kopt = []
+			for v in vals:
+				kopt.append(['-{}'.format(k), "{}".format(v)])
+			nopts.append(kopt)
+
+	return nopts
+
+if __name__ == "__main__":
+	# ================================================================================
+	# parse command line arguments
+	formats = ['raw', 'csv']
+	parser = argparse.ArgumentParser(description='Python Script to implement R.M.I.T. testing : Randomized Multiple Interleaved Trials')
+	parser.add_argument('--list', help='List all the commands that would be run', action='store_true')
+	parser.add_argument('--format', help='How to print the result', choices=formats, default='csv')
+	parser.add_argument('--file', nargs='?', type=argparse.FileType('w'), default=sys.stdout)
+	parser.add_argument('-t', '--trials', help='Number of trials to run per combinaison', type=int, default=3)
+	parser.add_argument('-o','--option',action='append')
+	parser.add_argument('command', metavar='command', type=str, nargs=1, help='the command prefix to run')
+	parser.add_argument('candidates', metavar='candidates', type=str, nargs='*', help='the candidate suffix to run')
+
+	try:
+		options =  parser.parse_args()
+	except:
+		print('ERROR: invalid arguments', file=sys.stderr)
+		parser.print_help(sys.stderr)
+		sys.exit(1)
+
+	# ================================================================================
+	# Identify the commands to run
+	commands = ["./" + options.command[0] + "-" + c for c in options.candidates]
+	for c in commands:
+		if not os.path.isfile(c):
+			print('ERROR: invalid command {}, file does not exist'.format(c), file=sys.stderr)
+			sys.exit(1)
+
+		if not os.access(c, os.X_OK):
+			print('ERROR: invalid command {}, file not executable'.format(c), file=sys.stderr)
+			sys.exit(1)
+
+
+	# ================================================================================
+	# Identify the options to run
+	opts = dict([parse_option(o) for o in options.option])
+
+	# Evaluate the options (options can depend on the value of other options)
+	opts = eval_options(opts)
+
+	# ================================================================================
+	# Figure out all the combinations to run
+	actions = []
+	for p in itertools.product(range(options.trials), commands, *opts):
+		act = [p[1]]
+		for o in p[2:]:
+			act.extend(o)
+		actions.append(act)
+
+	# ================================================================================
+	# Figure out all the combinations to run
+	if options.list:
+		for a in actions:
+			print(" ".join(a))
+		sys.exit(0)
+
+
+	# ================================================================================
+	# Prepare to run
+	random.shuffle(actions)
+
+	print("Running {} trials".format(len(actions)))
+	result = []
+
+	# ================================================================================
+	# Run
+	for i, a in enumerate(actions):
+		sa = " ".join(a)
+		print("{}/{} : {}          \r".format(i, len(actions), sa), end = '')
+		fields = {}
+		with subprocess.Popen( a, stdout  = subprocess.PIPE, stderr  = subprocess.PIPE) as proc:
+			out, err = proc.communicate()
+			if proc.returncode != 0:
+				print("ERROR: command '{}' encountered error, returned code {}".format(sa, proc.returncode), file=sys.stderr)
+				print(err.decode("utf-8"))
+				sys.exit(1)
+			for s in out.decode("utf-8").splitlines():
+				match = re.search("^(.*):(.*)$", s)
+				if match:
+					fields[match.group(1).strip()] = float(match.group(2).strip().replace(',',''))
+
+		result.append([a[0][2:], sa, fields])
+
+	print("Done                                                                                ")
+
+	# ================================================================================
+	# Print csv
+	if options.format == 'raw':
+		for r in result:
+			print(r, file=options.file)
+		sys.exit(0)
+
+	# ================================================================================
+	# Print csv
+	if options.format == 'csv':
+		# Clean result
+		headers = ["series", "command"]
+		data = []
+		first = True
+		for r in result:
+			if first:
+				first = False
+				headers.extend(r[2].keys())
+			else :
+				pass
+
+			d = [r[0], r[1]]
+			for k in headers[2:]:
+				d.append(r[2][k])
+
+			data.append(d)
+
+		# Print csv
+		print(",\t".join(headers), file=options.file)
+		for d in data:
+			print(",\t".join(["{}".format(dd) for dd in d]), file=options.file)
+		sys.exit(0)
Index: doc/papers/concurrency/mail2
===================================================================
--- doc/papers/concurrency/mail2	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ doc/papers/concurrency/mail2	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -1,2 +1,3 @@
+
 Date: Wed, 26 Jun 2019 20:12:38 +0000
 From: Aaron Thomas <onbehalfof@manuscriptcentral.com>
@@ -1074,2 +1075,213 @@
 Software: Practice and Experience Editorial Office
 
+
+
+Date: Thu, 15 Oct 2020 13:48:52 +0000
+From: Richard Jones <onbehalfof@manuscriptcentral.com>
+Reply-To: R.E.Jones@kent.ac.uk
+To: tdelisle@uwaterloo.ca, pabuhr@uwaterloo.ca
+Subject: Software: Practice and Experience - Decision on Manuscript ID
+ SPE-19-0219.R3
+
+15-Oct-2020
+
+Dear Dr Buhr,
+
+It is a pleasure to accept your manuscript entitled "Advanced Control-flow and Concurrency in Cforall" in its current form for publication in Software: Practice and Experience.  
+
+Please note although the manuscript is accepted the files will now be checked to ensure that everything is ready for publication, and you may be contacted if final versions of files for publication are required.
+
+Your article cannot be published until the publisher has received the appropriate signed license agreement. Within the next few days the corresponding author will receive an email from Wiley's Author Services system which will ask them to log in and will present them with the appropriate license for completion.
+
+Thank you for your fine contribution.
+
+Sincerely,
+Richard
+
+Prof. Richard Jones
+Editor, Software: Practice and Experience
+R.E.Jones@kent.ac.uk
+
+P.S. - You can help your research get the attention it deserves! Check out Wiley's free Promotion Guide for best-practice recommendations for promoting your work at www.wileyauthors.com/eeo/guide. And learn more about Wiley Editing Services which offers professional video, design, and writing services to create shareable video abstracts, infographics, conference posters, lay summaries, and research news stories for your research at www.wileyauthors.com/eeo/promotion.
+
+This journal accepts artwork submissions for Cover Images. This is an optional service you can use to help increase article exposure and showcase your research. For more information, including artwork guidelines, pricing, and submission details, please visit the Journal Cover Image page at www.wileyauthors.com/eeo/covers. If you want help creating an image, Wiley Editing Services offers a professional cover image design service that creates eye-catching images, ready to be showcased on the journal cover at www.wileyauthors.com/eeo/design.
+
+
+
+Date: Fri, 16 Oct 2020 12:44:42 +0000
+From: Mayank Roy Chowdhury <onbehalfof@manuscriptcentral.com>
+Reply-To: speoffice@wiley.com
+To: pabuhr@uwaterloo.ca
+Subject: Manuscript Accepted - Please submit final updates to SPE-19-0219.R3 [email ref: ENR-AW-1-c]
+
+16-Oct-2020
+
+Dear Dr. Buhr,
+
+Manuscript id: SPE-19-0219.R3
+Manuscript title: Advanced Control-flow and Concurrency in Cforall
+
+Although your manuscript has been accepted for publication it is now being returned to your author center for you to review and make any final adjustments or corrections prior to production and publication.
+
+Any special instructions will be listed below:
+1) Funding Information added in ScholorOne but missing in main document, Kindly add the Funding information in main document.
+2) Please provide the clean version of the manuscript without any highlights or tracked changes.
+3) Kindly check and make sure citations for all figures and Tables are present in the main document
+
+Please now log back into your Scholar One Author Center and click on the "Manuscripts Accepted for First Look" queue. In order to update the submission, click on the "submit updated manuscript" link in the "Actions" column and follow the steps as you would during a manuscript submission process.
+
+On the File Upload screen please upload the FINAL versions of all the files, including print quality image files. For information about image quality requirements, please refer to the guidelines at https://authorservices.wiley.com/asset/photos/electronic_artwork_guidelines.pdf.
+
+Instructions for uploading replacement files:
+1. On the "File Upload" step, click on the "edit" button for the file you wish to replace.
+2. In the "Upload a later version" section, browse to locate the replacement final version.
+3. Add any comments concerning the replacement (e.g. "high res image").
+4. Select whether the new file is a minor or major version (we suggest you select minor version)
+5. Click upload.
+6. Click 'Submit' when all the files have been uploaded and you will receive an automated email to say that submission is successful.
+
+Please submit your updates within the next 7 days to ensure there are no unnecessary delays in production.
+
+Sincerely,
+Software: Practice and Experience Editorial Office
+
+
+
+From: SPE Office <speoffice@wiley.com>
+To: "Peter A. Buhr" <pabuhr@uwaterloo.ca>
+Subject: Re: Manuscript Accepted - Please submit final updates to SPE-19-0219.R3 [email ref: ENR-AW-1-c]
+Date: Mon, 19 Oct 2020 17:04:24 +0000
+
+Dear Dr. Buhr,
+
+Thank you very much for contacting the Editorial Office.
+
+I would like to let you know that the files has been found in order and moved to production.
+
+Plesae let me know for further assistance in this regard.
+
+Best Regards
+
+Mayank Roy Chowdhury
+Editorial Assistant
+Software practice and Experience
+________________________________
+From: Peter A. Buhr <pabuhr@uwaterloo.ca>
+Sent: Sunday, October 18, 2020 2:00 PM
+To: SPE Office <speoffice@wiley.com>
+Cc: Thierry Delisle <tdelisle@uwaterloo.ca>
+Subject: Re: Manuscript Accepted - Please submit final updates to SPE-19-0219.R3 [email ref: ENR-AW-1-c]
+
+       This is an external email.
+
+    Mayank Roy Chowdhury <onbehalfof@manuscriptcentral.com> writes:
+
+    Instructions for uploading replacement files:
+    1. On the "File Upload" step, click on the "edit" button for the file you wish to replace.
+    2. In the "Upload a later version" section, browse to locate the replacement final version.
+    3. Add any comments concerning the replacement (e.g. "high res image").
+    4. Select whether the new file is a minor or major version (we suggest you select minor version)
+    5. Click upload.
+    6. Click 'Submit' when all the files have been uploaded and you will receive an automated email to say that submission is successful.
+
+There was no "edit" button on the "File Upload" page, so I just upload the
+final version of the PDF and source files using the mechanism on the "File
+Upload" page and submitted that.
+
+
+
+Date: Tue, 20 Oct 2020 13:28:37 +0530
+To: "Dr. Peter Buhr" <pabuhr@uwaterloo.ca>
+From: jpcms@spi-global.com
+Subject: Information: Production Editor Contact Software:Practice and Experience  | Advanced Control-flow and Concurrency in C A
+
+Dear Dr. Peter Buhr,
+
+We are in the process of preparing "Advanced Control-flow and Concurrency in C A" for publication. Your production editor, Joel Pacaanas, will support you and your article throughout the process.
+
+Please get in touch with your Production Editor at SPEproofs@wiley.com;EllaMae.Navor@spi-global.com if you have any questions.
+		
+Sincerely,
+Booking-in Team,
+On behalf of Wiley
+
+Article ID: SPE_2925
+Article DOI: 10.1002/SPE.2925
+
+
+
+Date: Tue, 20 Oct 2020 10:33:04 +0000
+From: <cs-author@wiley.com>
+To: <pabuhr@uwaterloo.ca>
+Subject: In Production: Your article accepted in Software: Practice and Experience
+
+Dear Peter Buhr,
+
+Article ID: SPE2925
+Article DOI: 10.1002/spe.2925
+Internal Article ID: 16922213
+Article: Advanced Control-flow and Concurrency in C A
+Journal: Software: Practice and Experience
+
+Congratulations on the acceptance of your article for publication in Software: Practice and Experience. 
+
+Your article has been received and the production process is now underway. We look forward to working with you and publishing your article. Using Wiley Author Services, you can track your article's progress.
+
+Please click below to login - if you are using a different email address than this one, you will need to manually assign this article to your Dashboard (see https://hub.wiley.com/docs/support/assigning-a-missing-article-to-my-dashboard-DOC-11871?utm_source=new%20user%20invitation&utm_medium=email How do I assign a missing article to My Dashboard?): 
+
+https://authorservices.wiley.com/index.html#login?campaign=email_invitation-new
+
+If applicable, a list of available actions will appear below - check out your Author Services Dashboard for all actions related to your articles.
+
+Sign your license agreement (REQUIRED)  -- you will receive an email when this task is ready on your dashboard. Track your article's progress to publicationAccess your published articleInvite colleagues to view your published article
+If you need any assistance, please click http://www.wileyauthors.com/help?utm_source=new%20user%20invitation&utm_medium=email here to view our Help section.
+
+Sincerely,
+Wiley Author Services
+
+P.S. - Some journals accept artwork submissions for Cover Images. This is an optional service you can use to help increase article exposure and showcase your research. Pricing and placement options vary by journal. For more information, including artwork guidelines, pricing, and submission details, please visit the https://authorservices.wiley.com/author-resources/Journal-Authors/Promotion/journal-cover-image.html?utm_source=as&utm_medium=email&utm_term=invitation_msg&utm_content=covers&utm_campaign=2019feb?campaign=email_invitation-new" target=_blank">Journal Cover Image page. If you want help creating an image, Wiley Editing Services offers a professional https://wileyeditingservices.com/en/article-promotion/cover-image-design.html?utm_source=as&utm_medium=email&utm_term=ie&utm_content=cid&utm_campaign=prodops" target=_blank">Cover Image Design service that creates eye-catching images, ready to be showcased on the journal cover.
+
+
+
+Date: Thu, 22 Oct 2020 20:21:49 +0000
+From: <cs-author@wiley.com>
+To: <pabuhr@uwaterloo.ca>
+Subject: You have actions to complete in Author Services
+
+Dear Peter Buhr,
+
+Article ID: SPE2925
+Article DOI: 10.1002/spe.2925
+Internal Article ID: 16922213
+Article: Advanced Control-flow and Concurrency in C A
+Journal: Software: Practice and Experience
+
+For the above article, you have the following open tasks:
+
+Sign your license agreement in order to publish your article. Simply click the Sign License button on your https://authorservices.wiley.com?campaign=email_license-notice1">Wiley Author Services Dashboard.
+
+Need any help? Please visit our https://authorsupport.wiley.com/s/">Author Support Center. 
+
+Sincerely,
+Wiley Author Services
+
+
+
+Date: Thu, 22 Oct 2020 23:13:07 +0000
+From: <cs-author@wiley.com>
+To: <pabuhr@uwaterloo.ca>
+Subject: License was successfully submitted! Thank you!
+
+Dear Peter Buhr,                                                                  
+
+Article ID: SPE2925
+Article DOI: 10.1002/spe.2925
+Internal Article ID: 16922213
+Article: Advanced Control-flow and Concurrency in C A  
+Journal: Software: Practice and Experience                                                                      
+                                                                          
+You've successfully completed license signing for your article - thank you! You can view your signed agreement at any time by visiting your https://authorservices.wiley.com?campaign=email_license-confirm">Wiley Author Services Dashboard.                                                                      
+
+Sincerely,                                                                                 
+
+Wiley Author Services
Index: doc/proposals/vtable.md
===================================================================
--- doc/proposals/vtable.md	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ doc/proposals/vtable.md	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -512,4 +512,28 @@
 possibly like the one used to create the assertion.
 
+### Extension: Associated Types Use
+If the `associated_types.md` proposal is accepted the following trait could
+be added:
+
+    trait is_virtual(dtype T) {
+        dtype table;
+        // An example assertion:
+        const table & get_virtual_table(T &);
+    }
+
+There may be more assertions but there has to be at least one way to find
+the (possibly default) virtual table. It is required to construct instances
+of the type.
+
+Without the assotiated type it would look like this:
+
+    trait is_virtual(dtype T, dtype table) {
+        const table & get_virtual_table(T &);
+    }
+
+Which is just a little bit longer to use but becomes more problematic if the
+user has to explicately provide the table's name as it doesn't really have its
+own type name. If it does it is probably mangled.
+
 ### Virtual Tables as Types
 Here we consider encoding plus the implementation of functions on it to be a
Index: doc/theses/andrew_beach_MMath/thesis-frontpgs.tex
===================================================================
--- doc/theses/andrew_beach_MMath/thesis-frontpgs.tex	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ doc/theses/andrew_beach_MMath/thesis-frontpgs.tex	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -5,4 +5,12 @@
 % TITLE PAGE
 %----------------------------------------------------------------------
+
+% Slowly generalizing.
+\ethesissetup{
+  author=Pat Neugraad,%
+  title={University of Waterloo E-Thesis Template for \LaTeX},
+  degree=phd,%
+  program=Zoology,%
+}
 
 \pagestyle{empty}
@@ -15,27 +23,23 @@
         \vspace*{1.0cm}
 
-        \Huge
-        {\bf University of Waterloo E-Thesis Template for \LaTeX }
-
-        \vspace*{1.0cm}
-
-        \normalsize
+        {\Huge\bf \eprint{title}}
+
+        \vspace*{1.0cm}
+
         by \\
 
         \vspace*{1.0cm}
 
-        \Large
-        Pat Neugraad \\
+        {\Large \eprint{author}} \\
 
         \vspace*{3.0cm}
 
-        \normalsize
         A thesis \\
         presented to the University of Waterloo \\ 
         in fulfillment of the \\
         thesis requirement for the degree of \\
-        Doctor of Philosophy \\
+        \eprint{degree} \\
         in \\
-        Zoology \\
+        \eprint{program} \\
 
         \vspace*{2.0cm}
@@ -45,5 +49,5 @@
         \vspace*{1.0cm}
 
-        \copyright\ Pat Neugraad 2017 \\
+        \copyright{} \eprint{author} 2017 \\
         \end{center}
 \end{titlepage}
Index: doc/theses/andrew_beach_MMath/uw-ethesis.cls
===================================================================
--- doc/theses/andrew_beach_MMath/uw-ethesis.cls	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ doc/theses/andrew_beach_MMath/uw-ethesis.cls	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -19,4 +19,9 @@
 %
 % Exported Names:
+%   \ethesissetup{<key-value-pairs>}
+%     Preforms set-up (or a reconfiguration) of the document class. See the
+%     Set-Up Keys section for the keys that may be passed in. Use commas to
+%     seperate key-value-pairs.
+%
 %   \ifformat{<format>}{<true>}{<false>}
 %     If the document's format is <format> than expands to <true> otherwise
@@ -27,13 +32,27 @@
 %     initial setup depends on the document format but they can be overriden
 %     with options in <setup> (set hyperref's \hypersetup for details).
+%
+%   \eprint{<key>}
+%     Expands to a human readable value tracked by the ethesis class. This
+%     can be used to retreave and format values set during set up.
+%
+% Set-Up Keys:
+%   author=<text>
+%   title=<text>
+%   program=<text>
+%   subject=<text>
+%   keywords=<text>
+%   degree=masters|phd
+%   faculty=ahs|arts|eng|env|math|sci
 \NeedsTeXFormat{LaTeX2e}
-\ProvidesClass{uw-ethesis}[2020/03/24 v0.1 UW-eThesis Template Document Class]
+\ProvidesClass{uw-ethesis}[2020/10/25 v0.2 UW-eThesis Template Document Class]
 
 \RequirePackage{etoolbox}
+\RequirePackage{xkeyval}
 
 % Requested Format:
-\newrobustcmd*{\ethesis@format}{digital}
-\DeclareOption{print}{\renewrobustcmd*{\ethesis@format}{print}}
-\DeclareOption{digital}{\renewrobustcmd*{\ethesis@format}{digital}}
+\newrobustcmd*{\ethesis@@format}{digital}
+\DeclareOption{print}{\renewrobustcmd*{\ethesis@@format}{print}}
+\DeclareOption{digital}{\renewrobustcmd*{\ethesis@@format}{digital}}
 
 \ProcessOptions\relax
@@ -81,7 +100,7 @@
 % a recto page. This will often require an empty verso (left-hand side) page
 % that should not have the page number printed on it.
-\let\origdoublepage\cleardoublepage
+\let\ethesis@origdoublepage\cleardoublepage
 \newcommand{\clearemptydoublepage}{%
-  \clearpage{\pagestyle{empty}\origdoublepage}}
+  \clearpage{\pagestyle{empty}\ethesis@origdoublepage}}
 \let\cleardoublepage\clearemptydoublepage
 
@@ -89,9 +108,24 @@
 \renewcommand*{\bibname}{References}
 
-% Configurations
-\def\setThesisTitle#1{\newrobustcmd*{\ethesis@title}{#1}}
-\def\setThesisAuthor#1{\newrobustcmd*{\ethesis@author}{#1}}
-\def\setThesisSubject#1{\newrobustcmd*{\ethesis@subject}{#1}}
-\def\setThesisKeywords#1{\newrobustcmd*{\ethesis@keywords}{#1}}
+\newrobustcmd*\ethesissetup[1]{\setkeys{ethesis}{#1}}
+
+\define@cmdkeys{ethesis}[ethesis@@]{%
+  author,title,program,subject,keywords}
+
+\define@choicekey{ethesis}{degree}{masters,phd}{\def\ethesis@@degree{#1}}
+\define@choicekey{ethesis}{faculty}{ahs,arts,eng,env,math,sci}%
+  {\def\ethesis@@faculty{#1}}
+
+\newrobustcmd*\eprint[1]{
+  \ifcsdef{ethesis@long#1}{\csuse{ethesis@long#1}}{%
+    \ifcsdef{ethesis@@#1}{\csuse{ethesis@@#1}}{%
+      % ERROR: (Check for a way to emit an actual error.)
+      [UW-eThesis doesn't know how to print: #1 ]
+    }
+  }
+}
+
+\newrobustcmd*\ethesis@longdegree{%
+  \ifdefstring{\ethesis@@degree}{phd}{Doctor of Philosophy}{Masters}}
 
 % Includes the hyperref package loading a number of defaults.
@@ -106,8 +140,8 @@
     pdfstartview={FitH},    % Fits the width of the page to the window.
   }
-  \ifdef{\ethesis@title}{\hypersetup{pdftitle={\ethesis@title}}}{}
-  \ifdef{\ethesis@author}{\hypersetup{pdfauthor={\ethesis@author}}}{}
-  \ifdef{\ethesis@subject}{\hypersetup{pdfsubject={\ethesis@subject}}}{}
-  \ifdef{\ethesis@keywords}{\hypersetup{pdfkeywords={\ethesis@keywords}}}{}
+  \ifdef{\ethesis@@title}{\hypersetup{pdftitle={\ethesis@@title}}}{}
+  \ifdef{\ethesis@@author}{\hypersetup{pdfauthor={\ethesis@@author}}}{}
+  \ifdef{\ethesis@@subject}{\hypersetup{pdfsubject={\ethesis@@subject}}}{}
+  \ifdef{\ethesis@@keywords}{\hypersetup{pdfkeywords={\ethesis@@keywords}}}{}
   \ifformat{print}{
     \hypersetup{
Index: libcfa/prelude/builtins.c
===================================================================
--- libcfa/prelude/builtins.c	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ libcfa/prelude/builtins.c	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -9,8 +9,10 @@
 // Author           : Peter A. Buhr
 // Created On       : Fri Jul 21 16:21:03 2017
-// Last Modified By : Peter A. Buhr
-// Last Modified On : Fri Oct  9 18:26:19 2020
-// Update Count     : 110
+// Last Modified By : Andrew Beach
+// Last Modified On : Tue Oct 27 14:42:00 2020
+// Update Count     : 111
 //
+
+#define __cforall_builtins__
 
 // type that wraps a pointer and a destructor-like function - used in generating implicit destructor calls for struct members in user-defined functions
Index: libcfa/src/concurrency/coroutine.cfa
===================================================================
--- libcfa/src/concurrency/coroutine.cfa	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ libcfa/src/concurrency/coroutine.cfa	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -10,6 +10,6 @@
 // Created On       : Mon Nov 28 12:27:26 2016
 // Last Modified By : Peter A. Buhr
-// Last Modified On : Tue May 26 22:06:09 2020
-// Update Count     : 21
+// Last Modified On : Fri Oct 23 23:05:24 2020
+// Update Count     : 22
 //
 
@@ -24,12 +24,8 @@
 #include <unistd.h>
 #include <sys/mman.h>									// mprotect
-extern "C" {
-// use this define to make unwind.h play nice, definitely a hack
-#define HIDE_EXPORTS
 #include <unwind.h>
-#undef HIDE_EXPORTS
-}
 
 #include "kernel_private.hfa"
+#include "exception.hfa"
 
 #define __CFA_INVOKE_PRIVATE__
@@ -49,10 +45,4 @@
 FORALL_DATA_INSTANCE(CoroutineCancelled, (dtype coroutine_t), (coroutine_t))
 
-struct __cfaehm_node {
-	struct _Unwind_Exception unwind_exception;
-	struct __cfaehm_node * next;
-	int handler_index;
-};
-
 forall(dtype T)
 void mark_exception(CoroutineCancelled(T) *) {}
@@ -60,4 +50,5 @@
 forall(dtype T)
 void copy(CoroutineCancelled(T) * dst, CoroutineCancelled(T) * src) {
+	dst->virtual_table = src->virtual_table;
 	dst->the_coroutine = src->the_coroutine;
 	dst->the_exception = src->the_exception;
@@ -74,5 +65,5 @@
 	verify( desc->cancellation );
 	desc->state = Cancelled;
-	exception_t * except = (exception_t *)(1 + (__cfaehm_node *)desc->cancellation);
+	exception_t * except = __cfaehm_cancellation_exception( desc->cancellation );
 
 	// TODO: Remove explitate vtable set once trac#186 is fixed.
@@ -217,5 +208,5 @@
 		size = libFloor(create_size - stack_data_size - diff, libAlign());
 	} // if
-	assertf( size >= MinStackSize, "Stack size %zd provides less than minimum of %d bytes for a stack.", size, MinStackSize );
+	assertf( size >= MinStackSize, "Stack size %zd provides less than minimum of %zd bytes for a stack.", size, MinStackSize );
 
 	this->storage = (__stack_t *)((intptr_t)storage + size);
Index: libcfa/src/concurrency/exception.cfa
===================================================================
--- libcfa/src/concurrency/exception.cfa	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ libcfa/src/concurrency/exception.cfa	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -10,20 +10,18 @@
 // Created On       : Mon Aug 17 10:41:00 2020
 // Last Modified By : Andrew Beach
-// Last Modified On : Tue Aug 25 14:41:00 2020
-// Update Count     : 0
+// Last Modified On : Wed Oct 28 14:34:00 2020
+// Update Count     : 1
 //
 
-extern "C" {
-// use this define to make unwind.h play nice, definitely a hack
-#define HIDE_EXPORTS
-#include <unwind.h>
-#undef HIDE_EXPORTS
-}
+#define __cforall_thread__
 
-#include "invoke.h"
 #include "exception.hfa"
+
 #include "coroutine.hfa"
 
 extern struct $thread * mainThread;
+extern "C" {
+extern void __cfactx_thrd_leave();
+}
 
 // Common pattern for all the stop functions, wait until the end then act.
@@ -52,6 +50,6 @@
 
 STOP_AT_END_FUNCTION(thread_cancelstop,
-	// TODO: Instead pass information to the joiner.
-	abort();
+	__cfactx_thrd_leave();
+	__cabi_abort( "Resumed cancelled thread" );
 )
 
@@ -85,4 +83,6 @@
 		stop_param = (void *)0x22;
 	} else {
+		this_thread->self_cor.cancellation = unwind_exception;
+
 		stop_func = thread_cancelstop;
 		stop_param = this_thread;
Index: libcfa/src/concurrency/exception.hfa
===================================================================
--- libcfa/src/concurrency/exception.hfa	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ libcfa/src/concurrency/exception.hfa	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -16,13 +16,14 @@
 #pragma once
 
+// This is an internal bridge between the two modes and must be C compatable.
+
+#include <unwind.h>
 #include "bits/defs.hfa"
 #include "invoke.h"
+#include "exception.h"
 
 #ifdef __cforall
 extern "C" {
-
-#define HIDE_EXPORTS
 #endif
-#include "unwind.h"
 
 struct exception_context_t * this_exception_context(void) OPTIONAL_THREAD;
@@ -32,5 +33,4 @@
 
 #ifdef __cforall
-#undef HIDE_EXPORTS
 }
 #endif
Index: libcfa/src/concurrency/invoke.h
===================================================================
--- libcfa/src/concurrency/invoke.h	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ libcfa/src/concurrency/invoke.h	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -157,4 +157,8 @@
 
 		// current execution status for coroutine
+		// Possible values are:
+		//    - TICKET_BLOCKED (-1) thread is blocked
+		//    - TICKET_RUNNING ( 0) thread is running
+		//    - TICKET_UNBLOCK ( 1) thread should ignore next block
 		volatile int ticket;
 		enum __Coroutine_State state:8;
Index: libcfa/src/concurrency/io/setup.cfa
===================================================================
--- libcfa/src/concurrency/io/setup.cfa	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ libcfa/src/concurrency/io/setup.cfa	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -250,5 +250,5 @@
 					// Fixup the thread state
 					thrd.state = Blocked;
-					thrd.ticket = 0;
+					thrd.ticket = TICKET_BLOCKED;
 					thrd.preempted = __NO_PREEMPTION;
 
Index: libcfa/src/concurrency/kernel.cfa
===================================================================
--- libcfa/src/concurrency/kernel.cfa	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ libcfa/src/concurrency/kernel.cfa	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -252,4 +252,5 @@
 		/* paranoid */ verify( kernelTLS.this_thread == thrd_dst );
 		/* paranoid */ verify( thrd_dst->context.SP );
+		/* paranoid */ verify( thrd_dst->state != Halted );
 		/* paranoid */ verifyf( ((uintptr_t)thrd_dst->context.SP) < ((uintptr_t)__get_stack(thrd_dst->curr_cor)->base ) || thrd_dst->curr_cor == proc_cor, "ERROR : Destination $thread %p has been corrupted.\n StackPointer too small.\n", thrd_dst ); // add escape condition if we are setting up the processor
 		/* paranoid */ verifyf( ((uintptr_t)thrd_dst->context.SP) > ((uintptr_t)__get_stack(thrd_dst->curr_cor)->limit) || thrd_dst->curr_cor == proc_cor, "ERROR : Destination $thread %p has been corrupted.\n StackPointer too large.\n", thrd_dst ); // add escape condition if we are setting up the processor
@@ -287,7 +288,6 @@
 		if(unlikely(thrd_dst->state == Halted)) {
 			// The thread has halted, it should never be scheduled/run again
-			// We may need to wake someone up here since
-			unpark( this->destroyer );
-			this->destroyer = 0p;
+			// finish the thread
+			__thread_finish( thrd_dst );
 			break RUNNING;
 		}
@@ -299,8 +299,8 @@
 		int old_ticket = __atomic_fetch_sub(&thrd_dst->ticket, 1, __ATOMIC_SEQ_CST);
 		switch(old_ticket) {
-			case 1:
+			case TICKET_RUNNING:
 				// This is case 1, the regular case, nothing more is needed
 				break RUNNING;
-			case 2:
+			case TICKET_UNBLOCK:
 				// This is case 2, the racy case, someone tried to run this thread before it finished blocking
 				// In this case, just run it again.
@@ -410,8 +410,8 @@
 	int old_ticket = __atomic_fetch_add(&thrd->ticket, 1, __ATOMIC_SEQ_CST);
 	switch(old_ticket) {
-		case 1:
+		case TICKET_RUNNING:
 			// Wake won the race, the thread will reschedule/rerun itself
 			break;
-		case 0:
+		case TICKET_BLOCKED:
 			/* paranoid */ verify( ! thrd->preempted != __NO_PREEMPTION );
 			/* paranoid */ verify( thrd->state == Blocked );
@@ -422,5 +422,5 @@
 		default:
 			// This makes no sense, something is wrong abort
-			abort();
+			abort("Thread %p (%s) has mismatch park/unpark\n", thrd, thrd->self_cor.name);
 	}
 }
@@ -448,9 +448,28 @@
 }
 
-// KERNEL ONLY
-void __leave_thread() {
-	/* paranoid */ verify( ! kernelTLS.preemption_state.enabled );
-	returnToKernel();
-	abort();
+extern "C" {
+	// Leave the thread monitor
+	// last routine called by a thread.
+	// Should never return
+	void __cfactx_thrd_leave() {
+		$thread * thrd = TL_GET( this_thread );
+		$monitor * this = &thrd->self_mon;
+
+		// Lock the monitor now
+		lock( this->lock __cfaabi_dbg_ctx2 );
+
+		disable_interrupts();
+
+		thrd->state = Halted;
+
+		if( thrd != this->owner || this->recursion != 1) { abort( "Thread internal monitor has unbalanced recursion" ); }
+
+		// Leave the thread
+		/* paranoid */ verify( ! kernelTLS.preemption_state.enabled );
+		returnToKernel();
+		abort();
+
+		// Control flow should never reach here!
+	}
 }
 
Index: libcfa/src/concurrency/kernel.hfa
===================================================================
--- libcfa/src/concurrency/kernel.hfa	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ libcfa/src/concurrency/kernel.hfa	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -79,8 +79,4 @@
 	// Handle to pthreads
 	pthread_t kernel_thread;
-
-	// RunThread data
-	// Action to do after a thread is ran
-	$thread * destroyer;
 
 	// Preemption data
Index: libcfa/src/concurrency/kernel/startup.cfa
===================================================================
--- libcfa/src/concurrency/kernel/startup.cfa	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ libcfa/src/concurrency/kernel/startup.cfa	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -441,5 +441,5 @@
 
 static void ?{}( $thread & this, current_stack_info_t * info) with( this ) {
-	ticket = 1;
+	ticket = TICKET_RUNNING;
 	state = Start;
 	self_cor{ info };
@@ -474,5 +474,4 @@
 	this.cltr = &_cltr;
 	full_proc = true;
-	destroyer = 0p;
 	do_terminate = false;
 	preemption_alarm = 0p;
Index: libcfa/src/concurrency/kernel_private.hfa
===================================================================
--- libcfa/src/concurrency/kernel_private.hfa	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ libcfa/src/concurrency/kernel_private.hfa	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -39,6 +39,6 @@
 ;
 
-//Block current thread and release/wake-up the following resources
-void __leave_thread() __attribute__((noreturn));
+//release/wake-up the following resources
+void __thread_finish( $thread * thrd );
 
 //-----------------------------------------------------------------------------
@@ -65,4 +65,8 @@
 // KERNEL ONLY unpark with out disabling interrupts
 void __unpark( struct __processor_id_t *, $thread * thrd );
+
+#define TICKET_BLOCKED (-1) // thread is blocked
+#define TICKET_RUNNING ( 0) // thread is running
+#define TICKET_UNBLOCK ( 1) // thread should ignore next block
 
 static inline bool __post(single_sem & this, struct __processor_id_t * id) {
Index: libcfa/src/concurrency/monitor.cfa
===================================================================
--- libcfa/src/concurrency/monitor.cfa	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ libcfa/src/concurrency/monitor.cfa	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -281,58 +281,29 @@
 }
 
-extern "C" {
-	// Leave the thread monitor
-	// last routine called by a thread.
-	// Should never return
-	void __cfactx_thrd_leave() {
-		$thread * thrd = TL_GET( this_thread );
-		$monitor * this = &thrd->self_mon;
-
-		// Lock the monitor now
-		lock( this->lock __cfaabi_dbg_ctx2 );
-
-		disable_interrupts();
-
-		thrd->state = Halted;
-
-		/* paranoid */ verifyf( thrd == this->owner, "Expected owner to be %p, got %p (r: %i, m: %p)", thrd, this->owner, this->recursion, this );
-
-		// Leaving a recursion level, decrement the counter
-		this->recursion -= 1;
-
-		// If we haven't left the last level of recursion
-		// it must mean there is an error
-		if( this->recursion != 0) { abort( "Thread internal monitor has unbalanced recursion" ); }
-
-		// Fetch the next thread, can be null
-		$thread * new_owner = next_thread( this );
-
-		// Release the monitor lock
-		unlock( this->lock );
-
-		// Unpark the next owner if needed
-		/* paranoid */ verifyf( !new_owner || new_owner == this->owner, "Expected owner to be %p, got %p (m: %p)", new_owner, this->owner, this );
-		/* paranoid */ verify( ! kernelTLS.preemption_state.enabled );
-		/* paranoid */ verify( ! kernelTLS.this_processor->destroyer );
-		/* paranoid */ verify( thrd->state == Halted );
-
-		kernelTLS.this_processor->destroyer = new_owner;
-
-		// Leave the thread
-		__leave_thread();
-
-		// Control flow should never reach here!
-	}
-}
-
-// Join a thread
-forall( dtype T | is_thread(T) )
-T & join( T & this ) {
-	$monitor *    m = get_monitor(this);
-	void (*dtor)(T& mutex this) = ^?{};
-	monitor_dtor_guard_t __guard = { &m, (fptr_t)dtor, true };
-	{
-		return this;
-	}
+void __thread_finish( $thread * thrd ) {
+	$monitor * this = &thrd->self_mon;
+
+	// Lock the monitor now
+	/* paranoid */ verify( this->lock.lock );
+	/* paranoid */ verifyf( thrd == this->owner, "Expected owner to be %p, got %p (r: %i, m: %p)", thrd, this->owner, this->recursion, this );
+	/* paranoid */ verify( ! kernelTLS.preemption_state.enabled );
+	/* paranoid */ verify( thrd->state == Halted );
+	/* paranoid */ verify( this->recursion == 1 );
+
+	// Leaving a recursion level, decrement the counter
+	this->recursion -= 1;
+	this->owner = 0p;
+
+	// Fetch the next thread, can be null
+	$thread * new_owner = next_thread( this );
+
+	// Release the monitor lock
+	unlock( this->lock );
+
+	// Unpark the next owner if needed
+	/* paranoid */ verifyf( !new_owner || new_owner == this->owner, "Expected owner to be %p, got %p (m: %p)", new_owner, this->owner, this );
+	/* paranoid */ verify( ! kernelTLS.preemption_state.enabled );
+	/* paranoid */ verify( thrd->state == Halted );
+	unpark( new_owner );
 }
 
Index: libcfa/src/concurrency/thread.cfa
===================================================================
--- libcfa/src/concurrency/thread.cfa	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ libcfa/src/concurrency/thread.cfa	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -19,4 +19,5 @@
 
 #include "kernel_private.hfa"
+#include "exception.hfa"
 
 #define __CFA_INVOKE_PRIVATE__
@@ -28,5 +29,5 @@
 	context{ 0p, 0p };
 	self_cor{ name, storage, storageSize };
-	ticket = 1;
+	ticket = TICKET_RUNNING;
 	state = Start;
 	preempted = __NO_PREEMPTION;
@@ -56,4 +57,60 @@
 	unregister(curr_cluster, this);
 	^self_cor{};
+}
+
+FORALL_DATA_INSTANCE(ThreadCancelled, (dtype thread_t), (thread_t))
+
+forall(dtype T)
+void copy(ThreadCancelled(T) * dst, ThreadCancelled(T) * src) {
+	dst->virtual_table = src->virtual_table;
+	dst->the_thread = src->the_thread;
+	dst->the_exception = src->the_exception;
+}
+
+forall(dtype T)
+const char * msg(ThreadCancelled(T) *) {
+	return "ThreadCancelled";
+}
+
+forall(dtype T)
+static void default_thread_cancel_handler(ThreadCancelled(T) & ) {
+	abort( "Unhandled thread cancellation.\n" );
+}
+
+forall(dtype T | is_thread(T) | IS_EXCEPTION(ThreadCancelled, (T)))
+void ?{}( thread_dtor_guard_t & this,
+		T & thrd, void(*defaultResumptionHandler)(ThreadCancelled(T) &)) {
+	$monitor * m = get_monitor(thrd);
+	void (*dtor)(T& mutex this) = ^?{};
+	bool join = defaultResumptionHandler != (void(*)(ThreadCancelled(T)&))0;
+	(this.mg){&m, (void(*)())dtor, join};
+
+	// After the guard set-up and any wait, check for cancellation.
+	$thread * desc = get_thread(thrd);
+	struct _Unwind_Exception * cancellation = desc->self_cor.cancellation;
+	if ( likely( 0p == cancellation ) ) {
+		return;
+	} else if ( Cancelled == desc->state ) {
+		return;
+	}
+	desc->state = Cancelled;
+	if (!join) {
+		defaultResumptionHandler = default_thread_cancel_handler;
+	}
+
+	ThreadCancelled(T) except;
+	// TODO: Remove explitate vtable set once trac#186 is fixed.
+	except.virtual_table = &get_exception_vtable(&except);
+	except.the_thread = &thrd;
+	except.the_exception = __cfaehm_cancellation_exception( cancellation );
+	throwResume except;
+
+	except.the_exception->virtual_table->free( except.the_exception );
+	free( cancellation );
+	desc->self_cor.cancellation = 0p;
+}
+
+void ^?{}( thread_dtor_guard_t & this ) {
+	^(this.mg){};
 }
 
@@ -93,4 +150,11 @@
 }
 
+//-----------------------------------------------------------------------------
+forall(dtype T | is_thread(T) | IS_RESUMPTION_EXCEPTION(ThreadCancelled, (T)))
+T & join( T & this ) {
+	thread_dtor_guard_t guard = { this, defaultResumptionHandler };
+	return this;
+}
+
 // Local Variables: //
 // mode: c //
Index: libcfa/src/concurrency/thread.hfa
===================================================================
--- libcfa/src/concurrency/thread.hfa	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ libcfa/src/concurrency/thread.hfa	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -22,12 +22,24 @@
 #include "kernel.hfa"
 #include "monitor.hfa"
+#include "exception.hfa"
 
 //-----------------------------------------------------------------------------
 // thread trait
 trait is_thread(dtype T) {
-      void ^?{}(T& mutex this);
-      void main(T& this);
-      $thread* get_thread(T& this);
+	void ^?{}(T& mutex this);
+	void main(T& this);
+	$thread* get_thread(T& this);
 };
+
+FORALL_DATA_EXCEPTION(ThreadCancelled, (dtype thread_t), (thread_t)) (
+	thread_t * the_thread;
+	exception_t * the_exception;
+);
+
+forall(dtype T)
+void copy(ThreadCancelled(T) * dst, ThreadCancelled(T) * src);
+
+forall(dtype T)
+const char * msg(ThreadCancelled(T) *);
 
 // define that satisfies the trait without using the thread keyword
@@ -65,4 +77,12 @@
 static inline void ?{}($thread & this, const char * const name, struct cluster & cl )                   { this{ name, cl, 0p, 65000 }; }
 static inline void ?{}($thread & this, const char * const name, struct cluster & cl, size_t stackSize ) { this{ name, cl, 0p, stackSize }; }
+
+struct thread_dtor_guard_t {
+	monitor_dtor_guard_t mg;
+};
+
+forall( dtype T | is_thread(T) | IS_EXCEPTION(ThreadCancelled, (T)) )
+void ?{}( thread_dtor_guard_t & this, T & thrd, void(*)(ThreadCancelled(T) &) );
+void ^?{}( thread_dtor_guard_t & this );
 
 //-----------------------------------------------------------------------------
@@ -108,5 +128,5 @@
 //----------
 // join
-forall( dtype T | is_thread(T) )
+forall( dtype T | is_thread(T) | IS_RESUMPTION_EXCEPTION(ThreadCancelled, (T)) )
 T & join( T & this );
 
Index: libcfa/src/exception.c
===================================================================
--- libcfa/src/exception.c	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ libcfa/src/exception.c	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -9,7 +9,7 @@
 // Author           : Andrew Beach
 // Created On       : Mon Jun 26 15:13:00 2017
-// Last Modified By : Peter A. Buhr
-// Last Modified On : Sat Aug 29 15:52:22 2020
-// Update Count     : 34
+// Last Modified By : Andrew Beach
+// Last Modified On : Tue Oct 27 16:27:00 2020
+// Update Count     : 35
 //
 
@@ -17,9 +17,10 @@
 #include <stddef.h> // for size_t
 
+#include <unwind.h> // for struct _Unwind_Exception {...};
+
 #include "exception.h"
 
 #include <stdlib.h>
 #include <stdio.h>
-#include <unwind.h>
 #include <bits/debug.hfa>
 #include "concurrency/invoke.h"
@@ -113,10 +114,4 @@
 
 // MEMORY MANAGEMENT =========================================================
-
-struct __cfaehm_node {
-	struct _Unwind_Exception unwind_exception;
-	struct __cfaehm_node * next;
-	int handler_index;
-};
 
 #define NODE_TO_EXCEPT(node) ((exception_t *)(1 + (node)))
Index: libcfa/src/exception.h
===================================================================
--- libcfa/src/exception.h	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ libcfa/src/exception.h	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -5,19 +5,25 @@
 // file "LICENCE" distributed with Cforall.
 //
-// exception.h -- Builtins for exception handling.
+// exception.h -- Internal exception handling definitions.
 //
 // Author           : Andrew Beach
 // Created On       : Mon Jun 26 15:11:00 2017
 // Last Modified By : Andrew Beach
-// Last Modified On : Tue May 19 14:17:00 2020
-// Update Count     : 10
+// Last Modified On : Tue Oct 27 14:45:00 2020
+// Update Count     : 11
 //
 
 #pragma once
 
+// This could be considered several headers. All are internal to the exception
+// system but needed to depending on whether they are C/Cforall code and
+// whether or not they are part of the builtins.
 
 #ifdef __cforall
 extern "C" {
 #endif
+
+// Included in C code or the built-ins.
+#if !defined(__cforall) || defined(__cforall_builtins__)
 
 struct __cfaehm_base_exception_t;
@@ -47,7 +53,7 @@
 // Function catches termination exceptions.
 void __cfaehm_try_terminate(
-    void (*try_block)(),
-    void (*catch_block)(int index, exception_t * except),
-    int (*match_block)(exception_t * except));
+	void (*try_block)(),
+	void (*catch_block)(int index, exception_t * except),
+	int (*match_block)(exception_t * except));
 
 // Clean-up the exception in catch blocks.
@@ -56,20 +62,39 @@
 // Data structure creates a list of resume handlers.
 struct __cfaehm_try_resume_node {
-    struct __cfaehm_try_resume_node * next;
-    _Bool (*handler)(exception_t * except);
+	struct __cfaehm_try_resume_node * next;
+	_Bool (*handler)(exception_t * except);
 };
 
 // These act as constructor and destructor for the resume node.
 void __cfaehm_try_resume_setup(
-    struct __cfaehm_try_resume_node * node,
-    _Bool (*handler)(exception_t * except));
+	struct __cfaehm_try_resume_node * node,
+	_Bool (*handler)(exception_t * except));
 void __cfaehm_try_resume_cleanup(
-    struct __cfaehm_try_resume_node * node);
+	struct __cfaehm_try_resume_node * node);
 
 // Check for a standard way to call fake deconstructors.
 struct __cfaehm_cleanup_hook {};
 
+#endif
+
+// Included in C code and the library.
+#if !defined(__cforall) || !defined(__cforall_builtins__)
+struct __cfaehm_node {
+	struct _Unwind_Exception unwind_exception;
+	struct __cfaehm_node * next;
+	int handler_index;
+};
+
+static inline exception_t * __cfaehm_cancellation_exception(
+		struct _Unwind_Exception * unwind_exception ) {
+	return (exception_t *)(1 + (struct __cfaehm_node *)unwind_exception);
+}
+#endif
+
 #ifdef __cforall
 }
+
+// Built-ins not visible in C.
+#if defined(__cforall_builtins__)
 
 // Not all the built-ins can be expressed in C. These can't be
@@ -124,2 +149,4 @@
 
 #endif
+
+#endif
Index: libcfa/src/stdhdr/unwind.h
===================================================================
--- libcfa/src/stdhdr/unwind.h	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
+++ libcfa/src/stdhdr/unwind.h	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -0,0 +1,31 @@
+//
+// 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.
+//
+// unwind.h -- Safely include unwind.h from Cforall.
+//
+// Author           : Andrew Beach
+// Created On       : Wed Oct 28 11:25:00 2020
+// Last Modified By : Andrew Beach
+// Last Modified On : Wed Oct 28 14:11:00 2020
+// Update Count     : 0
+//
+
+#pragma once
+
+extern "C" {
+// This prevents some GCC pragmas that CFA can't handle from being generated.
+#define HIDE_EXPORTS
+
+// Always include the header and use its header guard.
+#include_next <unwind.h>
+
+#undef HIDE_EXPORTS
+}
+
+// Local Variables: //
+// mode: c //
+// tab-width: 4 //
+// End: //
Index: src/AST/Convert.cpp
===================================================================
--- src/AST/Convert.cpp	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ src/AST/Convert.cpp	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -47,8 +47,11 @@
 
 //================================================================================================
-namespace {
+namespace ast {
 
 // This is to preserve the FindSpecialDecls hack. It does not (and perhaps should not)
 // allow us to use the same stratagy in the new ast.
+// xxx - since convert back pass works, this concern seems to be unnecessary.
+
+// these need to be accessed in new FixInit now
 ast::Type * sizeType = nullptr;
 ast::FunctionDecl * dereferenceOperator = nullptr;
@@ -63,4 +66,9 @@
 	using Cache = std::unordered_map< const ast::Node *, BaseSyntaxNode * >;
 	Cache cache;
+
+	// Statements can no longer be shared.
+	// however, since StmtExprResult is now implemented, need to still maintain
+	// readonly references.
+	Cache readonlyCache;
 
 	template<typename T>
@@ -154,12 +162,12 @@
 	}
 
-	const ast::DeclWithType * visit( const ast::ObjectDecl * node ) override final {
-		auto&& bfwd = get<Expression>().accept1( node->bitfieldWidth );
-		auto&& type = get<Type>().accept1( node->type );
-		auto&& init = get<Initializer>().accept1( node->init );
-		auto&& attr = get<Attribute>().acceptL( node->attributes );
+	const ast::DeclWithType * visit( const ast::ObjectDecl * node ) override final {	
 		if ( inCache( node ) ) {
 			return nullptr;
 		}
+		auto bfwd = get<Expression>().accept1( node->bitfieldWidth );
+		auto type = get<Type>().accept1( node->type );
+		auto attr = get<Attribute>().acceptL( node->attributes );
+
 		auto decl = new ObjectDecl(
 			node->name,
@@ -168,9 +176,17 @@
 			bfwd,
 			type->clone(),
-			init,
+			nullptr, // prevent infinite loop
 			attr,
 			Type::FuncSpecifiers( node->funcSpec.val )
 		);
-		return declWithTypePostamble( decl, node );
+
+		// handles the case where node->init references itself
+		// xxx - does it really happen?
+		declWithTypePostamble(decl, node);
+		auto init = get<Initializer>().accept1( node->init );
+		decl->init = init;
+		
+		this->node = decl;
+		return nullptr;
 	}
 
@@ -205,8 +221,8 @@
 		decl->statements = get<CompoundStmt>().accept1( node->stmts );
 		decl->withExprs = get<Expression>().acceptL( node->withExprs );
-		if ( dereferenceOperator == node ) {
+		if ( ast::dereferenceOperator == node ) {
 			Validate::dereferenceOperator = decl;
 		}
-		if ( dtorStructDestroy == node ) {
+		if ( ast::dtorStructDestroy == node ) {
 			Validate::dtorStructDestroy = decl;
 		}
@@ -267,5 +283,5 @@
 		);
 
-		if ( dtorStruct == node ) {
+		if ( ast::dtorStruct == node ) {
 			Validate::dtorStruct = decl;
 		}
@@ -320,5 +336,7 @@
 
 	const ast::Stmt * stmtPostamble( Statement * stmt, const ast::Stmt * node ) {
-		cache.emplace( node, stmt );
+		// force statements in old tree to be unique.
+		// cache.emplace( node, stmt );
+		readonlyCache.emplace( node, stmt );
 		stmt->location = node->location;
 		stmt->labels = makeLabelL( stmt, node->labels );
@@ -337,5 +355,4 @@
 		if ( inCache( node ) ) return nullptr;
 		auto stmt = new ExprStmt( nullptr );
-		cache.emplace( node, stmt );
 		stmt->expr = get<Expression>().accept1( node->expr );
 		return stmtPostamble( stmt, node );
@@ -1011,7 +1028,6 @@
 		auto stmts = node->stmts;
 		// disable sharing between multiple StmtExprs explicitly.
-		if (inCache(stmts)) {
-			stmts = ast::deepCopy(stmts.get());
-		}
+		// this should no longer be true.
+
 		auto rslt = new StmtExpr(
 			get<CompoundStmt>().accept1(stmts)
@@ -1020,4 +1036,8 @@
 		rslt->returnDecls = get<ObjectDecl>().acceptL(node->returnDecls);
 		rslt->dtors       = get<Expression>().acceptL(node->dtors);
+		if (node->resultExpr) {
+			// this MUST be found by children visit
+			rslt->resultExpr  = strict_dynamic_cast<ExprStmt *>(readonlyCache.at(node->resultExpr));
+		}
 
 		auto expr = visitBaseExpr( node, rslt );
@@ -1036,5 +1056,5 @@
 
 		auto expr = visitBaseExpr( node, rslt );
-		this->node = expr;
+		this->node = expr->clone();
 		return nullptr;
 	}
@@ -1126,5 +1146,5 @@
 		auto type = new BasicType{ cv( node ), (BasicType::Kind)(unsigned)node->kind };
 		// I believe this should always be a BasicType.
-		if ( sizeType == node ) {
+		if ( ast::sizeType == node ) {
 			Validate::SizeType = type;
 		}
@@ -1529,4 +1549,6 @@
 
 		// function type is now derived from parameter decls instead of storing them
+
+		/*
 		auto ftype = new ast::FunctionType((ast::ArgumentFlag)old->type->isVarArgs, cv(old->type));
 		ftype->params.reserve(paramVars.size());
@@ -1540,5 +1562,8 @@
 		}
 		ftype->forall = std::move(forall);
-		visitType(old->type, ftype);
+		*/
+
+		// can function type have attributes? seems not to be the case.
+		// visitType(old->type, ftype);
 
 		auto decl = new ast::FunctionDecl{
@@ -1546,4 +1571,5 @@
 			old->name,
 			// GET_ACCEPT_1(type, FunctionType),
+			std::move(forall),
 			std::move(paramVars),
 			std::move(returnVars),
@@ -1552,8 +1578,9 @@
 			{ old->linkage.val },
 			GET_ACCEPT_V(attributes, Attribute),
-			{ old->get_funcSpec().val }
+			{ old->get_funcSpec().val },
+			old->type->isVarArgs
 		};
 
-		decl->type = ftype;
+		// decl->type = ftype;
 		cache.emplace( old, decl );
 
@@ -1570,9 +1597,9 @@
 
 		if ( Validate::dereferenceOperator == old ) {
-			dereferenceOperator = decl;
+			ast::dereferenceOperator = decl;
 		}
 
 		if ( Validate::dtorStructDestroy == old ) {
-			dtorStructDestroy = decl;
+			ast::dtorStructDestroy = decl;
 		}
 	}
@@ -1599,5 +1626,5 @@
 
 		if ( Validate::dtorStruct == old ) {
-			dtorStruct = decl;
+			ast::dtorStruct = decl;
 		}
 	}
@@ -2531,5 +2558,5 @@
 		// I believe this should always be a BasicType.
 		if ( Validate::SizeType == old ) {
-			sizeType = type;
+			ast::sizeType = type;
 		}
 		visitType( old, type );
Index: src/AST/Decl.cpp
===================================================================
--- src/AST/Decl.cpp	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ src/AST/Decl.cpp	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -48,4 +48,23 @@
 
 // --- FunctionDecl
+
+FunctionDecl::FunctionDecl( const CodeLocation & loc, const std::string & name, 
+		std::vector<ptr<TypeDecl>>&& forall,
+		std::vector<ptr<DeclWithType>>&& params, std::vector<ptr<DeclWithType>>&& returns,
+		CompoundStmt * stmts, Storage::Classes storage, Linkage::Spec linkage,
+		std::vector<ptr<Attribute>>&& attrs, Function::Specs fs, bool isVarArgs)
+	: DeclWithType( loc, name, storage, linkage, std::move(attrs), fs ), params(std::move(params)), returns(std::move(returns)),
+	  stmts( stmts ) {
+		  FunctionType * ftype = new FunctionType(static_cast<ArgumentFlag>(isVarArgs));
+		  for (auto & param : this->params) {
+			  ftype->params.emplace_back(param->get_type());
+		  }
+		  for (auto & ret : this->returns) {
+			  ftype->returns.emplace_back(ret->get_type());
+		  }
+		  ftype->forall = std::move(forall);
+		  this->type = ftype;
+	  }
+
 
 const Type * FunctionDecl::get_type() const { return type.get(); }
Index: src/AST/Decl.hpp
===================================================================
--- src/AST/Decl.hpp	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ src/AST/Decl.hpp	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -131,10 +131,10 @@
 	std::vector< ptr<Expr> > withExprs;
 
-	FunctionDecl( const CodeLocation & loc, const std::string & name, 
+	FunctionDecl( const CodeLocation & loc, const std::string & name, std::vector<ptr<TypeDecl>>&& forall,
 		std::vector<ptr<DeclWithType>>&& params, std::vector<ptr<DeclWithType>>&& returns,
 		CompoundStmt * stmts, Storage::Classes storage = {}, Linkage::Spec linkage = Linkage::C,
-		std::vector<ptr<Attribute>>&& attrs = {}, Function::Specs fs = {})
-	: DeclWithType( loc, name, storage, linkage, std::move(attrs), fs ), params(std::move(params)), returns(std::move(returns)),
-	  stmts( stmts ) {}
+		std::vector<ptr<Attribute>>&& attrs = {}, Function::Specs fs = {}, bool isVarArgs = false);
+	// : DeclWithType( loc, name, storage, linkage, std::move(attrs), fs ), params(std::move(params)), returns(std::move(returns)),
+	//  stmts( stmts ) {}
 
 	const Type * get_type() const override;
Index: src/AST/DeclReplacer.cpp
===================================================================
--- src/AST/DeclReplacer.cpp	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ src/AST/DeclReplacer.cpp	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -38,4 +38,14 @@
 			const ast::TypeInstType * previsit( const ast::TypeInstType * );
 		};
+
+		struct VarExprReplacer {
+		private:
+			const ExprMap & exprMap;
+			
+		public:
+			VarExprReplacer(const ExprMap & exprMap): exprMap (exprMap) {}
+
+			const Expr * postvisit (const VariableExpr *);
+		};
 	}
 
@@ -54,4 +64,9 @@
 		DeclMap declMap;
 		return replace( node, declMap, typeMap, debug );
+	}
+
+	const ast::Node * replace( const ast::Node * node, const ExprMap & exprMap) {
+		Pass<VarExprReplacer> replacer = {exprMap};
+		return node->accept( replacer );
 	}
 
@@ -88,4 +103,11 @@
 			return ninst;
 		}
+
+		const Expr * VarExprReplacer::postvisit( const VariableExpr * expr ) {
+			if (!exprMap.count(expr->var)) return expr;
+
+			return exprMap.at(expr->var);
+		}
+
 	}
 }
Index: src/AST/DeclReplacer.hpp
===================================================================
--- src/AST/DeclReplacer.hpp	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ src/AST/DeclReplacer.hpp	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -23,12 +23,15 @@
 	class DeclWithType;
 	class TypeDecl;
+	class Expr;
 
 	namespace DeclReplacer {
 		using DeclMap = std::unordered_map< const DeclWithType *, const DeclWithType * >;
 		using TypeMap = std::unordered_map< const TypeDecl *, const TypeDecl * >;
+		using ExprMap = std::unordered_map< const DeclWithType *, const Expr * >;
 
 		const Node * replace( const Node * node, const DeclMap & declMap, bool debug = false );
 		const Node * replace( const Node * node, const TypeMap & typeMap, bool debug = false );
 		const Node * replace( const Node * node, const DeclMap & declMap, const TypeMap & typeMap, bool debug = false );
+		const Node * replace( const Node * node, const ExprMap & exprMap);
 	}
 }
Index: src/AST/Expr.cpp
===================================================================
--- src/AST/Expr.cpp	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ src/AST/Expr.cpp	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -67,5 +67,5 @@
 // --- UntypedExpr
 
-UntypedExpr * UntypedExpr::createDeref( const CodeLocation & loc, Expr * arg ) {
+UntypedExpr * UntypedExpr::createDeref( const CodeLocation & loc, const Expr * arg ) {
 	assert( arg );
 
@@ -92,5 +92,5 @@
 }
 
-UntypedExpr * UntypedExpr::createAssign( const CodeLocation & loc, Expr * lhs, Expr * rhs ) {
+UntypedExpr * UntypedExpr::createAssign( const CodeLocation & loc, const Expr * lhs, const Expr * rhs ) {
 	assert( lhs && rhs );
 
@@ -102,4 +102,29 @@
 	}
 	return ret;
+}
+
+// --- VariableExpr
+
+VariableExpr::VariableExpr( const CodeLocation & loc )
+: Expr( loc ), var( nullptr ) {}
+
+VariableExpr::VariableExpr( const CodeLocation & loc, const DeclWithType * v )
+: Expr( loc ), var( v ) {
+	assert( var );
+	assert( var->get_type() );
+	result = shallowCopy( var->get_type() );
+}
+
+bool VariableExpr::get_lvalue() const {
+	// It isn't always an lvalue, but it is never an rvalue.
+	return true;
+}
+
+VariableExpr * VariableExpr::functionPointer(
+		const CodeLocation & loc, const FunctionDecl * decl ) {
+	// wrap usually-determined result type in a pointer
+	VariableExpr * funcExpr = new VariableExpr{ loc, decl };
+	funcExpr->result = new PointerType{ funcExpr->result };
+	return funcExpr;
 }
 
@@ -238,29 +263,4 @@
 }
 
-// --- VariableExpr
-
-VariableExpr::VariableExpr( const CodeLocation & loc )
-: Expr( loc ), var( nullptr ) {}
-
-VariableExpr::VariableExpr( const CodeLocation & loc, const DeclWithType * v )
-: Expr( loc ), var( v ) {
-	assert( var );
-	assert( var->get_type() );
-	result = shallowCopy( var->get_type() );
-}
-
-bool VariableExpr::get_lvalue() const {
-	// It isn't always an lvalue, but it is never an rvalue.
-	return true;
-}
-
-VariableExpr * VariableExpr::functionPointer(
-		const CodeLocation & loc, const FunctionDecl * decl ) {
-	// wrap usually-determined result type in a pointer
-	VariableExpr * funcExpr = new VariableExpr{ loc, decl };
-	funcExpr->result = new PointerType{ funcExpr->result };
-	return funcExpr;
-}
-
 // --- ConstantExpr
 
Index: src/AST/Expr.hpp
===================================================================
--- src/AST/Expr.hpp	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ src/AST/Expr.hpp	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -226,7 +226,7 @@
 
 	/// Creates a new dereference expression
-	static UntypedExpr * createDeref( const CodeLocation & loc, Expr * arg );
+	static UntypedExpr * createDeref( const CodeLocation & loc, const Expr * arg );
 	/// Creates a new assignment expression
-	static UntypedExpr * createAssign( const CodeLocation & loc, Expr * lhs, Expr * rhs );
+	static UntypedExpr * createAssign( const CodeLocation & loc, const Expr * lhs, const Expr * rhs );
 
 	const Expr * accept( Visitor & v ) const override { return v.visit( this ); }
@@ -247,4 +247,23 @@
 private:
 	NameExpr * clone() const override { return new NameExpr{ *this }; }
+	MUTATE_FRIEND
+};
+
+/// A reference to a named variable.
+class VariableExpr final : public Expr {
+public:
+	readonly<DeclWithType> var;
+
+	VariableExpr( const CodeLocation & loc );
+	VariableExpr( const CodeLocation & loc, const DeclWithType * v );
+
+	bool get_lvalue() const final;
+
+	/// generates a function pointer for a given function
+	static VariableExpr * functionPointer( const CodeLocation & loc, const FunctionDecl * decl );
+
+	const Expr * accept( Visitor & v ) const override { return v.visit( this ); }
+private:
+	VariableExpr * clone() const override { return new VariableExpr{ *this }; }
 	MUTATE_FRIEND
 };
@@ -392,23 +411,4 @@
 };
 
-/// A reference to a named variable.
-class VariableExpr final : public Expr {
-public:
-	readonly<DeclWithType> var;
-
-	VariableExpr( const CodeLocation & loc );
-	VariableExpr( const CodeLocation & loc, const DeclWithType * v );
-
-	bool get_lvalue() const final;
-
-	/// generates a function pointer for a given function
-	static VariableExpr * functionPointer( const CodeLocation & loc, const FunctionDecl * decl );
-
-	const Expr * accept( Visitor & v ) const override { return v.visit( this ); }
-private:
-	VariableExpr * clone() const override { return new VariableExpr{ *this }; }
-	MUTATE_FRIEND
-};
-
 /// A compile-time constant.
 /// Mostly carries C-source text from parse to code-gen, without interpretation.  E.g. strings keep their outer quotes and never have backslashes interpreted.
@@ -422,5 +422,5 @@
 		const CodeLocation & loc, const Type * ty, const std::string & r,
 			std::optional<unsigned long long> i )
-	: Expr( loc, ty ), rep( r ), ival( i ) {}
+	: Expr( loc, ty ), rep( r ), ival( i ), underlyer(ty) {}
 
 	/// Gets the integer value of this constant, if one is appropriate to its type.
@@ -617,5 +617,5 @@
 
 	ImplicitCopyCtorExpr( const CodeLocation& loc, const ApplicationExpr * call )
-	: Expr( loc, call->result ) { assert( call ); }
+	: Expr( loc, call->result ), callExpr(call) { assert( call ); assert(call->result); }
 
 	const Expr * accept( Visitor & v ) const override { return v.visit( this ); }
@@ -742,4 +742,6 @@
 	std::vector<ptr<Expr>> dtors;              ///< destructor(s) for return variable(s)
 
+	readonly<ExprStmt> resultExpr;
+
 	StmtExpr( const CodeLocation & loc, const CompoundStmt * ss );
 
Index: src/AST/Fwd.hpp
===================================================================
--- src/AST/Fwd.hpp	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ src/AST/Fwd.hpp	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -137,3 +137,8 @@
 typedef unsigned int UniqueId;
 
+extern Type * sizeType;
+extern FunctionDecl * dereferenceOperator;
+extern StructDecl   * dtorStruct;
+extern FunctionDecl * dtorStructDestroy;
+
 }
Index: src/AST/Node.hpp
===================================================================
--- src/AST/Node.hpp	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ src/AST/Node.hpp	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -49,4 +49,5 @@
 
 	bool unique() const { return strong_count == 1; }
+	bool isManaged() const {return strong_count > 0; }
 
 private:
Index: src/AST/Pass.hpp
===================================================================
--- src/AST/Pass.hpp	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ src/AST/Pass.hpp	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -228,4 +228,9 @@
 	template<typename core_type>
 	friend void accept_all( std::list< ptr<Decl> > & decls, Pass<core_type>& visitor );
+
+	bool isInFunction() const {
+		return inFunction;
+	}
+
 private:
 
@@ -235,4 +240,8 @@
 	const ast::Stmt * call_accept( const ast::Stmt * );
 	const ast::Expr * call_accept( const ast::Expr * );
+
+	// requests WithStmtsToAdd directly add to this statement, as if it is a compound.
+
+	const ast::Stmt * call_accept_as_compound(const ast::Stmt *);
 
 	template< typename node_t >
@@ -257,4 +266,7 @@
 	template<typename node_t, typename parent_t, typename child_t>
 	void maybe_accept(const node_t * &, child_t parent_t::* child);
+
+	template<typename node_t, typename parent_t, typename child_t>
+	void maybe_accept_as_compound(const node_t * &, child_t parent_t::* child);
 
 private:
@@ -284,4 +296,5 @@
 private:
 	bool inFunction = false;
+	bool atFunctionTop = false;
 };
 
@@ -371,4 +384,8 @@
 struct WithVisitorRef {
 	Pass<core_t> * const visitor = nullptr;
+
+	bool isInFunction() const {
+		return visitor->isInFunction();
+	}
 };
 
Index: src/AST/Pass.impl.hpp
===================================================================
--- src/AST/Pass.impl.hpp	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ src/AST/Pass.impl.hpp	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -167,4 +167,12 @@
 		__pedantic_pass_assert( stmt );
 
+		return stmt->accept( *this );
+	}
+
+	template< typename core_t >
+	const ast::Stmt * ast::Pass< core_t >::call_accept_as_compound( const ast::Stmt * stmt ) {
+		__pedantic_pass_assert( __visit_children() );
+		__pedantic_pass_assert( stmt );
+
 		// add a few useful symbols to the scope
 		using __pass::empty;
@@ -324,4 +332,28 @@
 
 		auto new_val = call_accept( old_val );
+
+		static_assert( !std::is_same<const ast::Node *, decltype(new_val)>::value || std::is_same<int, decltype(old_val)>::value, "ERROR");
+
+		if( __pass::differs(old_val, new_val) ) {
+			auto new_parent = __pass::mutate<core_t>(parent);
+			new_parent->*child = new_val;
+			parent = new_parent;
+		}
+	}
+
+	template< typename core_t >
+	template<typename node_t, typename parent_t, typename child_t>
+	void ast::Pass< core_t >::maybe_accept_as_compound(
+		const node_t * & parent,
+		child_t parent_t::*child
+	) {
+		static_assert( std::is_base_of<parent_t, node_t>::value, "Error deducing member object" );
+
+		if(__pass::skip(parent->*child)) return;
+		const auto & old_val = __pass::get(parent->*child, 0);
+
+		static_assert( !std::is_same<const ast::Node * &, decltype(old_val)>::value, "ERROR");
+
+		auto new_val = call_accept_as_compound( old_val );
 
 		static_assert( !std::is_same<const ast::Node *, decltype(new_val)>::value || std::is_same<int, decltype(old_val)>::value, "ERROR");
@@ -470,8 +502,11 @@
 				// foralls are still in function type
 				maybe_accept( node, &FunctionDecl::type );
-				// function body needs to have the same scope as parameters - CompoundStmt will not enter
-				// a new scope if inFunction is true
+				// First remember that we are now within a function.
 				ValueGuard< bool > oldInFunction( inFunction );
 				inFunction = true;
+				// The function body needs to have the same scope as parameters.
+				// A CompoundStmt will not enter a new scope if atFunctionTop is true.
+				ValueGuard< bool > oldAtFunctionTop( atFunctionTop );
+				atFunctionTop = true;
 				maybe_accept( node, &FunctionDecl::stmts );
 				maybe_accept( node, &FunctionDecl::attributes );
@@ -640,9 +675,9 @@
 	VISIT_START( node );
 	VISIT({
-		// do not enter a new scope if inFunction is true - needs to check old state before the assignment
-		auto guard1 = makeFuncGuard( [this, inFunctionCpy = this->inFunction]() {
-			if ( ! inFunctionCpy ) __pass::symtab::enter(core, 0);
-		}, [this, inFunctionCpy = this->inFunction]() {
-			if ( ! inFunctionCpy ) __pass::symtab::leave(core, 0);
+		// Do not enter (or leave) a new scope if atFunctionTop. Remember to save the result.
+		auto guard1 = makeFuncGuard( [this, enterScope = !this->atFunctionTop]() {
+			if ( enterScope ) __pass::symtab::enter(core, 0);
+		}, [this, leaveScope = !this->atFunctionTop]() {
+			if ( leaveScope ) __pass::symtab::leave(core, 0);
 		});
 		ValueGuard< bool > guard2( inFunction );
@@ -703,6 +738,6 @@
 		maybe_accept( node, &IfStmt::inits    );
 		maybe_accept( node, &IfStmt::cond     );
-		maybe_accept( node, &IfStmt::thenPart );
-		maybe_accept( node, &IfStmt::elsePart );
+		maybe_accept_as_compound( node, &IfStmt::thenPart );
+		maybe_accept_as_compound( node, &IfStmt::elsePart );
 	})
 
@@ -721,5 +756,5 @@
 		maybe_accept( node, &WhileStmt::inits );
 		maybe_accept( node, &WhileStmt::cond  );
-		maybe_accept( node, &WhileStmt::body  );
+		maybe_accept_as_compound( node, &WhileStmt::body  );
 	})
 
@@ -736,8 +771,9 @@
 		// for statements introduce a level of scope (for the initialization)
 		guard_symtab guard { *this };
+		// xxx - old ast does not create WithStmtsToAdd scope for loop inits. should revisit this later.
 		maybe_accept( node, &ForStmt::inits );
 		maybe_accept( node, &ForStmt::cond  );
 		maybe_accept( node, &ForStmt::inc   );
-		maybe_accept( node, &ForStmt::body  );
+		maybe_accept_as_compound( node, &ForStmt::body  );
 	})
 
@@ -834,5 +870,5 @@
 		maybe_accept( node, &CatchStmt::decl );
 		maybe_accept( node, &CatchStmt::cond );
-		maybe_accept( node, &CatchStmt::body );
+		maybe_accept_as_compound( node, &CatchStmt::body );
 	})
 
Index: src/AST/SymbolTable.cpp
===================================================================
--- src/AST/SymbolTable.cpp	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ src/AST/SymbolTable.cpp	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -335,11 +335,11 @@
 }
 
-/*
-void SymbolTable::addFunctionType( const FunctionType * ftype ) {
-	addTypes( ftype->forall );
-	addIds( ftype->returns );
-	addIds( ftype->params );
-}
-*/
+
+void SymbolTable::addFunction( const FunctionDecl * func ) {
+	addTypes( func->type->forall );
+	addIds( func->returns );
+	addIds( func->params );
+}
+
 
 void SymbolTable::lazyInitScope() {
Index: src/AST/SymbolTable.hpp
===================================================================
--- src/AST/SymbolTable.hpp	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ src/AST/SymbolTable.hpp	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -145,5 +145,5 @@
 
 	/// convenience function for adding all of the declarations in a function type to the indexer
-	// void addFunctionType( const FunctionType * ftype );
+	void addFunction( const FunctionDecl * );
 
 private:
Index: src/AST/TranslationUnit.hpp
===================================================================
--- src/AST/TranslationUnit.hpp	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
+++ src/AST/TranslationUnit.hpp	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -0,0 +1,44 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// TranslationUnit.hpp --
+//
+// Author           : Andrew Beach
+// Created On       : Tue Jun 11 15:30:00 2019
+// Last Modified By : Andrew Beach
+// Last Modified On : Tue Jun 11 15:42:00 2019
+// Update Count     : 0
+//
+
+#pragma once
+
+#include <map>
+#include <vector>
+
+#include "Fwd.hpp"
+
+namespace ast {
+
+struct TranslationUnit {
+	std::list< ptr< Decl > > decls;
+
+	struct Globals {
+		std::map< UniqueId, Decl * > idMap;
+
+		Type * sizeType;
+		FunctionDecl * dereference;
+		StructDecl * dtorStruct;
+		FunctionDecl * dtorDestroy;
+	} global;
+};
+
+}
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/AST/Type.cpp
===================================================================
--- src/AST/Type.cpp	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ src/AST/Type.cpp	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -157,4 +157,10 @@
 
 template<typename decl_t>
+SueInstType<decl_t>::SueInstType(
+	const base_type * b, std::vector<ptr<Expr>> && params,
+	CV::Qualifiers q, std::vector<ptr<Attribute>> && as )
+: BaseInstType( b->name, std::move(params), q, std::move(as) ), base( b ) {}
+
+template<typename decl_t>
 bool SueInstType<decl_t>::isComplete() const {
 	return base ? base->body : false;
Index: src/AST/Type.hpp
===================================================================
--- src/AST/Type.hpp	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ src/AST/Type.hpp	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -302,7 +302,4 @@
 class FunctionType final : public ParameterizedType {
 public:
-//	std::vector<ptr<DeclWithType>> returns;
-//	std::vector<ptr<DeclWithType>> params;
-
 	std::vector<ptr<Type>> returns;
 	std::vector<ptr<Type>> params;
@@ -345,4 +342,9 @@
 	: ParameterizedType(q, std::move(as)), params(), name(n) {}
 
+	BaseInstType(
+		const std::string& n, std::vector<ptr<Expr>> && params,
+		CV::Qualifiers q = {}, std::vector<ptr<Attribute>> && as = {} )
+	: ParameterizedType(q, std::move(as)), params(std::move(params)), name(n) {}
+
 	BaseInstType( const BaseInstType & o );
 
@@ -369,5 +371,9 @@
 
 	SueInstType(
-		const decl_t * b, CV::Qualifiers q = {}, std::vector<ptr<Attribute>> && as = {} );
+		const base_type * b, CV::Qualifiers q = {}, std::vector<ptr<Attribute>> && as = {} );
+
+	SueInstType(
+		const base_type * b, std::vector<ptr<Expr>> && params,
+		CV::Qualifiers q = {}, std::vector<ptr<Attribute>> && as = {} );
 
 	bool isComplete() const override;
Index: src/AST/porting.md
===================================================================
--- src/AST/porting.md	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ src/AST/porting.md	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -30,4 +30,8 @@
   * Base nodes now override `const Node * accept( Visitor & v ) const = 0` with, e.g. `const Stmt * accept( Visitor & v ) const override = 0`
 * `PassVisitor` is replaced with `ast::Pass`
+  * Most one shot uses can use `ast::Pass::run` and `ast::Pass::read`.
+
+`WithConstTypeSubstitution`
+* `env` => `typeSubs`
 
 ## Structural Changes ##
@@ -146,4 +150,9 @@
   * allows `newObject` as just default settings
 
+`FunctionDecl`
+* `params` and `returns` added.
+  * Contain the declarations of the parameters and return variables.
+  * Types should match (even be shared with) the fields of `type`.
+
 `NamedTypeDecl`
 * `parameters` => `params`
@@ -154,4 +163,7 @@
 `AggregateDecl`
 * `parameters` => `params`
+
+`StructDecl`
+* `makeInst` replaced by better constructor on `StructInstType`.
 
 `Expr`
@@ -245,5 +257,5 @@
 * **TODO** move `kind`, `typeNames` into code generator
 
-`ReferenceToType`
+`ReferenceToType` => `BaseInstType`
 * deleted `get_baseParameters()` from children
   * replace with `aggr() ? aggr()->params : nullptr`
@@ -261,5 +273,10 @@
 * `returnVals` => `returns`
 * `parameters` => `params`
+  * Both now just point at types.
 * `bool isVarArgs;` => `enum ArgumentFlag { FixedArgs, VariableArgs }; ArgumentFlag isVarArgs;`
+
+`SueInstType`
+* Template class, with specializations and using to implement some other types:
+  * `StructInstType`, `UnionInstType` & `EnumInstType`
 
 `TypeInstType`
Index: src/Common/PassVisitor.h
===================================================================
--- src/Common/PassVisitor.h	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ src/Common/PassVisitor.h	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -354,6 +354,11 @@
 	virtual TypeSubstitution * mutate( TypeSubstitution * sub ) final;
 
+	bool isInFunction() const {
+		return inFunction;
+	}
+
 private:
 	bool inFunction = false;
+	bool atFunctionTop = false;
 
 	template<typename pass_t> friend void acceptAll( std::list< Declaration* > &decls, PassVisitor< pass_t >& visitor );
@@ -526,4 +531,8 @@
 public:
 	PassVisitor<pass_type> * const visitor = nullptr;
+
+	bool isInFunction() const {
+		return visitor->isInFunction();
+	}
 };
 
Index: src/Common/PassVisitor.impl.h
===================================================================
--- src/Common/PassVisitor.impl.h	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ src/Common/PassVisitor.impl.h	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -532,8 +532,11 @@
 			indexerAddId( &func );
 			maybeAccept_impl( node->type, *this );
-			// function body needs to have the same scope as parameters - CompoundStmt will not enter
-			// a new scope if inFunction is true
+			// First remember that we are now within a function.
 			ValueGuard< bool > oldInFunction( inFunction );
 			inFunction = true;
+			// The function body needs to have the same scope as parameters.
+			// A CompoundStmt will not enter a new scope if atFunctionTop is true.
+			ValueGuard< bool > oldAtFunctionTop( atFunctionTop );
+			atFunctionTop = true;
 			maybeAccept_impl( node->statements, *this );
 			maybeAccept_impl( node->attributes, *this );
@@ -567,8 +570,11 @@
 			indexerAddId( &func );
 			maybeAccept_impl( node->type, *this );
-			// function body needs to have the same scope as parameters - CompoundStmt will not enter
-			// a new scope if inFunction is true
+			// First remember that we are now within a function.
 			ValueGuard< bool > oldInFunction( inFunction );
 			inFunction = true;
+			// The function body needs to have the same scope as parameters.
+			// A CompoundStmt will not enter a new scope if atFunctionTop is true.
+			ValueGuard< bool > oldAtFunctionTop( atFunctionTop );
+			atFunctionTop = true;
 			maybeAccept_impl( node->statements, *this );
 			maybeAccept_impl( node->attributes, *this );
@@ -601,8 +607,11 @@
 			indexerAddId( &func );
 			maybeMutate_impl( node->type, *this );
-			// function body needs to have the same scope as parameters - CompoundStmt will not enter
-			// a new scope if inFunction is true
+			// First remember that we are now within a function.
 			ValueGuard< bool > oldInFunction( inFunction );
 			inFunction = true;
+			// The function body needs to have the same scope as parameters.
+			// A CompoundStmt will not enter a new scope if atFunctionTop is true.
+			ValueGuard< bool > oldAtFunctionTop( atFunctionTop );
+			atFunctionTop = true;
 			maybeMutate_impl( node->statements, *this );
 			maybeMutate_impl( node->attributes, *this );
@@ -1007,9 +1016,9 @@
 	VISIT_START( node );
 	{
-		// do not enter a new scope if inFunction is true - needs to check old state before the assignment
-		ValueGuard< bool > oldInFunction( inFunction );
-		auto guard1 = makeFuncGuard( [this, &oldInFunction]() { if ( ! oldInFunction.old ) indexerScopeEnter(); }, [this, &oldInFunction]() { if ( ! oldInFunction.old ) indexerScopeLeave(); } );
+		// Do not enter a new scope if atFunctionTop is true, don't leave one either.
+		ValueGuard< bool > oldAtFunctionTop( atFunctionTop );
+		auto guard1 = makeFuncGuard( [this, go = !atFunctionTop]() { if ( go ) indexerScopeEnter(); }, [this, go = !atFunctionTop]() { if ( go ) indexerScopeLeave(); } );
 		auto guard2 = makeFuncGuard( [this]() { call_beginScope();   }, [this]() { call_endScope();     } );
-		inFunction = false;
+		atFunctionTop = false;
 		visitStatementList( node->kids );
 	}
@@ -1021,9 +1030,9 @@
 	VISIT_START( node );
 	{
-		// do not enter a new scope if inFunction is true - needs to check old state before the assignment
-		ValueGuard< bool > oldInFunction( inFunction );
-		auto guard1 = makeFuncGuard( [this, &oldInFunction]() { if ( ! oldInFunction.old ) indexerScopeEnter(); }, [this, &oldInFunction]() { if ( ! oldInFunction.old ) indexerScopeLeave(); } );
+		// Do not enter a new scope if atFunctionTop is true, don't leave one either.
+		ValueGuard< bool > oldAtFunctionTop( atFunctionTop );
+		auto guard1 = makeFuncGuard( [this, go = !atFunctionTop]() { if ( go ) indexerScopeEnter(); }, [this, go = !atFunctionTop]() { if ( go ) indexerScopeLeave(); } );
 		auto guard2 = makeFuncGuard( [this]() { call_beginScope();   }, [this]() { call_endScope();     } );
-		inFunction = false;
+		atFunctionTop = false;
 		visitStatementList( node->kids );
 	}
@@ -1035,9 +1044,9 @@
 	MUTATE_START( node );
 	{
-		// do not enter a new scope if inFunction is true - needs to check old state before the assignment
-		ValueGuard< bool > oldInFunction( inFunction );
-		auto guard1 = makeFuncGuard( [this, &oldInFunction]() { if ( ! oldInFunction.old ) indexerScopeEnter(); }, [this, &oldInFunction]() { if ( ! oldInFunction.old ) indexerScopeLeave(); } );
+		// Do not enter a new scope if atFunctionTop is true, don't leave one either.
+		ValueGuard< bool > oldAtFunctionTop( atFunctionTop );
+		auto guard1 = makeFuncGuard( [this, go = !atFunctionTop]() { if ( go ) indexerScopeEnter(); }, [this, go = !atFunctionTop]() { if ( go ) indexerScopeLeave(); } );
 		auto guard2 = makeFuncGuard( [this]() { call_beginScope();   }, [this]() { call_endScope();     } );
-		inFunction = false;
+		atFunctionTop = false;
 		mutateStatementList( node->kids );
 	}
Index: src/Common/utility.h
===================================================================
--- src/Common/utility.h	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ src/Common/utility.h	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -360,7 +360,8 @@
 	reverse_iterate_t( T & ref ) : ref(ref) {}
 
-	typedef typename T::reverse_iterator iterator;
-	iterator begin() { return ref.rbegin(); }
-	iterator end() { return ref.rend(); }
+	// this does NOT work on const T!!!
+	// typedef typename T::reverse_iterator iterator;
+	auto begin() { return ref.rbegin(); }
+	auto end() { return ref.rend(); }
 };
 
Index: src/Concurrency/Keywords.cc
===================================================================
--- src/Concurrency/Keywords.cc	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ src/Concurrency/Keywords.cc	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -46,4 +46,12 @@
 	}
 
+	// Only detects threads constructed with the keyword thread.
+	inline static bool isThread( DeclarationWithType * decl ) {
+		Type * baseType = decl->get_type()->stripDeclarator();
+		StructInstType * instType = dynamic_cast<StructInstType *>( baseType );
+		if ( nullptr == instType ) { return false; }
+		return instType->baseStruct->is_thread();
+	}
+
 	//=============================================================================================
 	// Pass declarations
@@ -119,5 +127,5 @@
 			"get_thread",
 			"thread keyword requires threads to be in scope, add #include <thread.hfa>\n",
-			"",
+			"ThreadCancelled",
 			true,
 			AggregateDecl::Thread
@@ -290,6 +298,7 @@
 		std::list<DeclarationWithType*> findMutexArgs( FunctionDecl*, bool & first );
 		void validate( DeclarationWithType * );
-		void addDtorStatments( FunctionDecl* func, CompoundStmt *, const std::list<DeclarationWithType * > &);
-		void addStatments( FunctionDecl* func, CompoundStmt *, const std::list<DeclarationWithType * > &);
+		void addDtorStatements( FunctionDecl* func, CompoundStmt *, const std::list<DeclarationWithType * > &);
+		void addStatements( FunctionDecl* func, CompoundStmt *, const std::list<DeclarationWithType * > &);
+		void addThreadDtorStatements( FunctionDecl* func, CompoundStmt * body, const std::list<DeclarationWithType * > & args );
 
 		static void implement( std::list< Declaration * > & translationUnit ) {
@@ -302,4 +311,5 @@
 		StructDecl* guard_decl = nullptr;
 		StructDecl* dtor_guard_decl = nullptr;
+		StructDecl* thread_guard_decl = nullptr;
 
 		static std::unique_ptr< Type > generic_func;
@@ -801,5 +811,5 @@
 		bool first = false;
 		std::list<DeclarationWithType*> mutexArgs = findMutexArgs( decl, first );
-		bool isDtor = CodeGen::isDestructor( decl->name );
+		bool const isDtor = CodeGen::isDestructor( decl->name );
 
 		// Is this function relevant to monitors
@@ -849,9 +859,15 @@
 
 		// Instrument the body
-		if( isDtor ) {
-			addDtorStatments( decl, body, mutexArgs );
+		if ( isDtor && isThread( mutexArgs.front() ) ) {
+			if( !thread_guard_decl ) {
+				SemanticError( decl, "thread destructor requires threads to be in scope, add #include <thread.hfa>\n" );
+			}
+			addThreadDtorStatements( decl, body, mutexArgs );
+		}
+		else if ( isDtor ) {
+			addDtorStatements( decl, body, mutexArgs );
 		}
 		else {
-			addStatments( decl, body, mutexArgs );
+			addStatements( decl, body, mutexArgs );
 		}
 	}
@@ -870,4 +886,8 @@
 			assert( !dtor_guard_decl );
 			dtor_guard_decl = decl;
+		}
+		else if( decl->name == "thread_dtor_guard_t" && decl->body ) {
+			assert( !thread_guard_decl );
+			thread_guard_decl = decl;
 		}
 	}
@@ -908,5 +928,5 @@
 	}
 
-	void MutexKeyword::addDtorStatments( FunctionDecl* func, CompoundStmt * body, const std::list<DeclarationWithType * > & args ) {
+	void MutexKeyword::addDtorStatements( FunctionDecl* func, CompoundStmt * body, const std::list<DeclarationWithType * > & args ) {
 		Type * arg_type = args.front()->get_type()->clone();
 		arg_type->set_mutex( false );
@@ -957,8 +977,44 @@
 
 		//$monitor * __monitors[] = { get_monitor(a), get_monitor(b) };
-		body->push_front( new DeclStmt( monitors) );
-	}
-
-	void MutexKeyword::addStatments( FunctionDecl* func, CompoundStmt * body, const std::list<DeclarationWithType * > & args ) {
+		body->push_front( new DeclStmt( monitors ) );
+	}
+
+	void MutexKeyword::addThreadDtorStatements(
+			FunctionDecl*, CompoundStmt * body,
+			const std::list<DeclarationWithType * > & args ) {
+		assert( args.size() == 1 );
+		DeclarationWithType * arg = args.front();
+		Type * arg_type = arg->get_type()->clone();
+		assert( arg_type->get_mutex() );
+		arg_type->set_mutex( false );
+
+		// thread_dtor_guard_t __guard = { this, intptr( 0 ) };
+		body->push_front(
+			new DeclStmt( new ObjectDecl(
+				"__guard",
+				noStorageClasses,
+				LinkageSpec::Cforall,
+				nullptr,
+				new StructInstType(
+					noQualifiers,
+					thread_guard_decl
+				),
+				new ListInit(
+					{
+						new SingleInit( new CastExpr( new VariableExpr( arg ), arg_type ) ),
+						new SingleInit( new UntypedExpr(
+							new NameExpr( "intptr" ), {
+								new ConstantExpr( Constant::from_int( 0 ) ),
+							}
+						) ),
+					},
+					noDesignators,
+					true
+				)
+			))
+		);
+	}
+
+	void MutexKeyword::addStatements( FunctionDecl* func, CompoundStmt * body, const std::list<DeclarationWithType * > & args ) {
 		ObjectDecl * monitors = new ObjectDecl(
 			"__monitors",
Index: src/GenPoly/GenPoly.cc
===================================================================
--- src/GenPoly/GenPoly.cc	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ src/GenPoly/GenPoly.cc	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -46,4 +46,12 @@
 		}
 
+		bool hasPolyParams( const std::vector<ast::ptr<ast::Expr>> & params, const ast::TypeSubstitution * env) {
+			for (auto &param : params) {
+				auto paramType = param.strict_as<ast::TypeExpr>();
+				if (isPolyType(paramType->type, env)) return true;
+			}
+			return false;
+		}
+
 		/// Checks a parameter list for polymorphic parameters from tyVars; will substitute according to env if present
 		bool hasPolyParams( std::list< Expression* >& params, const TyVarMap &tyVars, const TypeSubstitution *env ) {
@@ -56,4 +64,12 @@
 		}
 
+		bool hasPolyParams( const std::vector<ast::ptr<ast::Expr>> & params, const TyVarMap & tyVars, const ast::TypeSubstitution * env) {
+			for (auto &param : params) {
+				auto paramType = param.strict_as<ast::TypeExpr>();
+				if (isPolyType(paramType->type, tyVars, env)) return true;
+			}
+			return false;
+		}
+
 		/// Checks a parameter list for dynamic-layout parameters from tyVars; will substitute according to env if present
 		bool hasDynParams( std::list< Expression* >& params, const TyVarMap &tyVars, const TypeSubstitution *env ) {
@@ -92,4 +108,13 @@
 			Type *newType = env->lookup( typeInst->get_name() );
 			if ( newType ) return newType;
+		}
+		return type;
+	}
+
+	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)) {
+			auto newType = env->lookup(typeInst->name);
+			if (newType) return newType;
 		}
 		return type;
@@ -111,4 +136,19 @@
 	}
 
+	const ast::Type * isPolyType(const ast::Type * type, const ast::TypeSubstitution * env) {
+		type = replaceTypeInst( type, env );
+
+		if ( dynamic_cast< const ast::TypeInstType * >( type ) ) {
+			return type;
+		} else if ( auto arrayType = dynamic_cast< const ast::ArrayType * >( type ) ) {
+			return isPolyType( arrayType->base, env );
+		} else if ( auto structType = dynamic_cast< const ast::StructInstType* >( type ) ) {
+			if ( hasPolyParams( structType->params, env ) ) return type;
+		} else if ( auto unionType = dynamic_cast< const ast::UnionInstType* >( type ) ) {
+			if ( hasPolyParams( unionType->params, env ) ) return type;
+		}
+		return 0;
+	}
+
 	Type *isPolyType( Type *type, const TyVarMap &tyVars, const TypeSubstitution *env ) {
 		type = replaceTypeInst( type, env );
@@ -126,4 +166,19 @@
 		}
 		return 0;
+	}
+
+	const ast::Type * isPolyType(const ast::Type * type, const TyVarMap & tyVars, const ast::TypeSubstitution * env) {
+		type = replaceTypeInst( type, env );
+
+		if ( auto typeInst = dynamic_cast< const ast::TypeInstType * >( type ) ) {
+			return tyVars.find(typeInst->name) != tyVars.end() ? type : nullptr;
+		} else if ( auto arrayType = dynamic_cast< const ast::ArrayType * >( type ) ) {
+			return isPolyType( arrayType->base, env );
+		} else if ( auto structType = dynamic_cast< const ast::StructInstType* >( type ) ) {
+			if ( hasPolyParams( structType->params, env ) ) return type;
+		} else if ( auto unionType = dynamic_cast< const ast::UnionInstType* >( type ) ) {
+			if ( hasPolyParams( unionType->params, env ) ) return type;
+		}
+		return nullptr;
 	}
 
@@ -449,4 +504,12 @@
 	}
 
+	namespace {
+		// temporary hack to avoid re-implementing anything related to TyVarMap
+		// does this work? these two structs have identical definitions.
+		inline TypeDecl::Data convData(const ast::TypeDecl::Data & data) {
+			return *reinterpret_cast<const TypeDecl::Data *>(&data);
+		}
+	}
+
 	bool needsBoxing( Type * param, Type * arg, const TyVarMap &exprTyVars, const TypeSubstitution * env ) {
 		// is parameter is not polymorphic, don't need to box
@@ -459,4 +522,13 @@
 	}
 
+	bool needsBoxing( const ast::Type * param, const ast::Type * arg, const TyVarMap &exprTyVars, const ast::TypeSubstitution * env) {
+		// is parameter is not polymorphic, don't need to box
+		if ( ! isPolyType( param, exprTyVars ) ) return false;
+		ast::ptr<ast::Type> newType = arg;
+		if ( env ) env->apply( newType );
+		// if the argument's type is polymorphic, we don't need to box again!
+		return ! isPolyType( newType );
+	}
+
 	bool needsBoxing( Type * param, Type * arg, ApplicationExpr * appExpr, const TypeSubstitution * env ) {
 		FunctionType * function = getFunctionType( appExpr->function->result );
@@ -467,6 +539,19 @@
 	}
 
+	bool needsBoxing( const ast::Type * param, const ast::Type * arg, const ast::ApplicationExpr * appExpr, const ast::TypeSubstitution * env) {
+		const ast::FunctionType * function = getFunctionType(appExpr->func->result);
+		assertf( function, "ApplicationExpr has non-function type: %s", toString( appExpr->func->result ).c_str() );
+		TyVarMap exprTyVars(TypeDecl::Data{});
+		makeTyVarMap(function, exprTyVars);
+		return needsBoxing(param, arg, exprTyVars, env);
+
+	}
+
 	void addToTyVarMap( TypeDecl * tyVar, TyVarMap &tyVarMap ) {
 		tyVarMap.insert( tyVar->name, TypeDecl::Data{ tyVar } );
+	}
+
+	void addToTyVarMap( const ast::TypeDecl * tyVar, TyVarMap & tyVarMap) {
+		tyVarMap.insert(tyVar->name, convData(ast::TypeDecl::Data{tyVar}));
 	}
 
@@ -478,4 +563,16 @@
 		if ( PointerType *pointer = dynamic_cast< PointerType* >( type ) ) {
 			makeTyVarMap( pointer->get_base(), tyVarMap );
+		}
+	}
+
+	void makeTyVarMap(const ast::Type * type, TyVarMap & tyVarMap) {
+		if (auto ptype = dynamic_cast<const ast::ParameterizedType *>(type)) {
+ 			for (auto & tyVar : ptype->forall) {
+				assert (tyVar);
+				addToTyVarMap(tyVar, tyVarMap);
+			}
+		}
+		if (auto pointer = dynamic_cast<const ast::PointerType *>(type)) {
+			makeTyVarMap(pointer->base, tyVarMap);
 		}
 	}
Index: src/GenPoly/GenPoly.h
===================================================================
--- src/GenPoly/GenPoly.h	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ src/GenPoly/GenPoly.h	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -26,6 +26,6 @@
 
 namespace GenPoly {
+
 	typedef ErasableScopedMap< std::string, TypeDecl::Data > TyVarMap;
-
 	/// Replaces a TypeInstType by its referrent in the environment, if applicable
 	Type* replaceTypeInst( Type* type, const TypeSubstitution* env );
@@ -33,7 +33,9 @@
 	/// returns polymorphic type if is polymorphic type, NULL otherwise; will look up substitution in env if provided
 	Type *isPolyType( Type *type, const TypeSubstitution *env = 0 );
+	const ast::Type * isPolyType(const ast::Type * type, const ast::TypeSubstitution * env = nullptr);
 
 	/// returns polymorphic type if is polymorphic type in tyVars, NULL otherwise; will look up substitution in env if provided
 	Type *isPolyType( Type *type, const TyVarMap &tyVars, const TypeSubstitution *env = 0 );
+	const ast::Type * isPolyType(const ast::Type * type, const TyVarMap & tyVars, const ast::TypeSubstitution * env = nullptr);
 
 	/// returns dynamic-layout type if is dynamic-layout type in tyVars, NULL otherwise; will look up substitution in env if provided
@@ -84,7 +86,9 @@
 	/// true if arg requires boxing given exprTyVars
 	bool needsBoxing( Type * param, Type * arg, const TyVarMap &exprTyVars, const TypeSubstitution * env );
+	bool needsBoxing( const ast::Type * param, const ast::Type * arg, const TyVarMap &exprTyVars, const ast::TypeSubstitution * env);
 
 	/// true if arg requires boxing in the call to appExpr
 	bool needsBoxing( Type * param, Type * arg, ApplicationExpr * appExpr, const TypeSubstitution * env );
+	bool needsBoxing( const ast::Type * param, const ast::Type * arg, const ast::ApplicationExpr * appExpr, const ast::TypeSubstitution * env);
 
 	/// Adds the type variable `tyVar` to `tyVarMap`
@@ -93,4 +97,5 @@
 	/// Adds the declarations in the forall list of type (and its pointed-to type if it's a pointer type) to `tyVarMap`
 	void makeTyVarMap( Type *type, TyVarMap &tyVarMap );
+	void makeTyVarMap(const ast::Type * type, TyVarMap & tyVarMap);
 
 	/// Prints type variable map
Index: src/GenPoly/Specialize.cc
===================================================================
--- src/GenPoly/Specialize.cc	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ src/GenPoly/Specialize.cc	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -218,4 +218,8 @@
 		thunkFunc->get_attributes().push_back( new Attribute( "unused" ) );
 
+		// Thunks at the global level must be static to avoid collisions between files.
+		// (Conversly thunks inside a function must be unique and not static.)
+		thunkFunc->storageClasses.is_static = !isInFunction();
+
 		// thread thunk parameters into call to actual function, naming thunk parameters as we go
 		UniqueName paramNamer( paramPrefix );
Index: src/InitTweak/FixGlobalInit.cc
===================================================================
--- src/InitTweak/FixGlobalInit.cc	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ src/InitTweak/FixGlobalInit.cc	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -34,4 +34,8 @@
 #include "SynTree/Visitor.h"       // for acceptAll, Visitor
 
+#include "AST/Expr.hpp"
+#include "AST/Node.hpp"
+#include "AST/Pass.hpp"
+
 namespace InitTweak {
 	class GlobalFixer : public WithShortCircuiting {
@@ -50,4 +54,18 @@
 		FunctionDecl * initFunction;
 		FunctionDecl * destroyFunction;
+	};
+
+	class GlobalFixer_new : 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;
 	};
 
@@ -91,4 +109,27 @@
 	}
 
+	void fixGlobalInit(std::list<ast::ptr<ast::Decl>> & translationUnit, bool inLibrary) {
+		ast::Pass<GlobalFixer_new> fixer;
+		accept_all(translationUnit, fixer);
+
+		if ( !fixer.core.initStmts.empty() ) {
+			std::vector<ast::ptr<ast::Expr>> ctorParams;
+			if (inLibrary) ctorParams.emplace_back(ast::ConstantExpr::from_int({}, 200));
+			auto initFunction = new ast::FunctionDecl({}, "__global_init__", {}, {}, {}, new ast::CompoundStmt({}, std::move(fixer.core.initStmts)), 
+				ast::Storage::Static, ast::Linkage::C, {new ast::Attribute("constructor", std::move(ctorParams))});
+
+			translationUnit.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({}, 200));
+			auto destroyFunction = new ast::FunctionDecl({}, "__global_destroy__", {}, {}, {}, new ast::CompoundStmt({}, std::move(fixer.core.destroyStmts)), 
+				ast::Storage::Static, ast::Linkage::C, {new ast::Attribute("destructor", std::move(dtorParams))});
+
+			translationUnit.emplace_back(destroyFunction);
+		} // if
+	}
+
 	void GlobalFixer::previsit( ObjectDecl *objDecl ) {
 		std::list< Statement * > & initStatements = initFunction->get_statements()->get_kids();
@@ -112,20 +153,5 @@
 			} // if
 			if ( Statement * ctor = ctorInit->ctor ) {
-				// Translation 1: Add this attribute on the global declaration:
-				//    __attribute__((section (".data#")))
-				// which makes gcc put the global in the data section,
-				// so that the global is writeable (via a const cast) in the init function.
-				// 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"
-				Type *strLitT = new PointerType( Type::Qualifiers( ),
-					new BasicType( Type::Qualifiers( ), BasicType::Char ) );
-				std::list< Expression * > attr_params;
-				attr_params.push_back( 
-					new ConstantExpr( Constant( strLitT, "\".data#\"", std::nullopt ) ) );
-				objDecl->attributes.push_back(new Attribute("section", attr_params));
-				// Translation 2: Move the initizliation off the global declaration,
-				// into the startup function.
+				addDataSectonAttribute( objDecl );
 				initStatements.push_back( ctor );
 				objDecl->init = nullptr;
@@ -142,4 +168,33 @@
 	}
 
+	void GlobalFixer_new::previsit(const ast::ObjectDecl * objDecl) {
+		auto mutDecl = mutate(objDecl);
+		assertf(mutDecl == objDecl, "Global object decl must be unique");
+		if ( auto ctorInit = objDecl->init.as<ast::ConstructorInit>() ) {
+			// 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 );
+				// ctorInit->dtor = nullptr;
+			} // if
+			if ( const ast::Stmt * ctor = ctorInit->ctor ) {
+				initStmts.push_back( ctor );
+				mutDecl->init = nullptr;
+				// ctorInit->ctor = nullptr;
+			} else if ( const ast::Init * init = ctorInit->init ) {
+				mutDecl->init = init;
+				// ctorInit->init = nullptr;
+			} else {
+				// no constructor and no initializer, which is okay
+				mutDecl->init = nullptr;
+			} // if
+			// delete ctorInit;
+		} // if
+	}
+
 	// only modify global variables
 	void GlobalFixer::previsit( FunctionDecl * ) { visit_children = false; }
Index: src/InitTweak/FixGlobalInit.h
===================================================================
--- src/InitTweak/FixGlobalInit.h	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ src/InitTweak/FixGlobalInit.h	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -19,4 +19,7 @@
 #include <string>  // for string
 
+#include <AST/Fwd.hpp>
+
+
 class Declaration;
 
@@ -26,4 +29,5 @@
 	/// function is for library code.
 	void fixGlobalInit( std::list< Declaration * > & translationUnit, bool inLibrary );
+	void fixGlobalInit( std::list< ast::ptr<ast::Decl> > & translationUnit, bool inLibrary );
 } // namespace
 
Index: src/InitTweak/FixInit.cc
===================================================================
--- src/InitTweak/FixInit.cc	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ src/InitTweak/FixInit.cc	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -219,5 +219,5 @@
 		};
 
-		struct SplitExpressions : public WithShortCircuiting, public WithTypeSubstitution, public WithStmtsToAdd {
+		struct SplitExpressions : public WithShortCircuiting, /*public WithTypeSubstitution, */public WithStmtsToAdd {
 			/// add CompoundStmts around top-level expressions so that temporaries are destroyed in the correct places.
 			static void split( std::list< Declaration * > &translationUnit );
@@ -802,4 +802,10 @@
 				if ( Statement * ctor = ctorInit->get_ctor() ) {
 					if ( objDecl->get_storageClasses().is_static ) {
+
+						// The ojbect needs to go in the data section, regardless of dtor complexity below.
+						// The attribute works, and is meant to apply, both for leaving the static local alone,
+						// and for hoisting it out as a static global.
+						addDataSectonAttribute( objDecl );
+
 						// originally wanted to take advantage of gcc nested functions, but
 						// we get memory errors with this approach. To remedy this, the static
Index: src/InitTweak/FixInit.h
===================================================================
--- src/InitTweak/FixInit.h	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ src/InitTweak/FixInit.h	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -19,4 +19,6 @@
 #include <string>  // for string
 
+#include <AST/Fwd.hpp>
+
 class Declaration;
 
@@ -24,4 +26,6 @@
 	/// replace constructor initializers with expression statements and unwrap basic C-style initializers
 	void fix( std::list< Declaration * > & translationUnit, bool inLibrary );
+
+	void fix( std::list<ast::ptr<ast::Decl>> & translationUnit, bool inLibrary);
 } // namespace
 
Index: src/InitTweak/FixInitNew.cpp
===================================================================
--- src/InitTweak/FixInitNew.cpp	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
+++ src/InitTweak/FixInitNew.cpp	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -0,0 +1,1439 @@
+#include "FixInit.h"
+
+#include <stddef.h>                    // for NULL
+#include <algorithm>                   // for set_difference, copy_if
+#include <cassert>                     // for assert, strict_dynamic_cast
+#include <iostream>                    // for operator<<, ostream, basic_ost...
+#include <iterator>                    // for insert_iterator, back_inserter
+#include <list>                        // for _List_iterator, list, list<>::...
+#include <map>                         // for _Rb_tree_iterator, _Rb_tree_co...
+#include <memory>                      // for allocator_traits<>::value_type
+#include <set>                         // for set, set<>::value_type
+#include <unordered_map>               // for unordered_map, unordered_map<>...
+#include <unordered_set>               // for unordered_set
+#include <utility>                     // for pair
+
+#include "CodeGen/GenType.h"           // for genPrettyType
+#include "CodeGen/OperatorTable.h"
+#include "Common/PassVisitor.h"        // for PassVisitor, WithStmtsToAdd
+#include "Common/SemanticError.h"      // for SemanticError
+#include "Common/UniqueName.h"         // for UniqueName
+#include "Common/utility.h"            // for CodeLocation, ValueGuard, toSt...
+#include "FixGlobalInit.h"             // for fixGlobalInit
+#include "GenInit.h"                   // for genCtorDtor
+#include "GenPoly/GenPoly.h"           // for getFunctionType
+#include "InitTweak.h"                 // for getFunctionName, getCallArg
+#include "ResolvExpr/Resolver.h"       // for findVoidExpression
+#include "ResolvExpr/typeops.h"        // for typesCompatible
+#include "SymTab/Autogen.h"            // for genImplicitCall
+#include "SymTab/Indexer.h"            // for Indexer
+#include "SymTab/Mangler.h"            // for Mangler
+#include "SynTree/LinkageSpec.h"       // for C, Spec, Cforall, isBuiltin
+#include "SynTree/Attribute.h"         // for Attribute
+#include "SynTree/Constant.h"          // for Constant
+#include "SynTree/Declaration.h"       // for ObjectDecl, FunctionDecl, Decl...
+#include "SynTree/Expression.h"        // for UniqueExpr, VariableExpr, Unty...
+#include "SynTree/Initializer.h"       // for ConstructorInit, SingleInit
+#include "SynTree/Label.h"             // for Label, operator<
+#include "SynTree/Mutator.h"           // for mutateAll, Mutator, maybeMutate
+#include "SynTree/Statement.h"         // for ExprStmt, CompoundStmt, Branch...
+#include "SynTree/Type.h"              // for Type, Type::StorageClasses
+#include "SynTree/TypeSubstitution.h"  // for TypeSubstitution, operator<<
+#include "SynTree/DeclReplacer.h"      // for DeclReplacer
+#include "SynTree/Visitor.h"           // for acceptAll, maybeAccept
+#include "Validate/FindSpecialDecls.h" // for dtorStmt, dtorStructDestroy
+
+#include "AST/Expr.hpp"
+#include "AST/Node.hpp"
+#include "AST/Pass.hpp"
+#include "AST/Print.hpp"
+#include "AST/SymbolTable.hpp"
+#include "AST/Type.hpp"
+#include "AST/DeclReplacer.hpp"
+
+extern bool ctordtorp; // print all debug
+extern bool ctorp; // print ctor debug
+extern bool cpctorp; // print copy ctor debug
+extern bool dtorp; // print dtor debug
+#define PRINT( text ) if ( ctordtorp ) { text }
+#define CP_CTOR_PRINT( text ) if ( ctordtorp || cpctorp ) { text }
+#define DTOR_PRINT( text ) if ( ctordtorp || dtorp ) { text }
+
+namespace InitTweak {
+	namespace {
+		struct SelfAssignChecker {
+			void previsit( const ast::ApplicationExpr * appExpr );
+		};
+
+		struct StmtExprResult {
+			static void link( std::list<ast::ptr<ast::Decl> > & translationUnit );
+
+			const ast::StmtExpr * previsit( const ast::StmtExpr * stmtExpr );
+		};
+
+		struct InsertImplicitCalls : public ast::WithConstTypeSubstitution, public ast::WithShortCircuiting {
+			/// wrap function application expressions as ImplicitCopyCtorExpr nodes so that it is easy to identify which
+			/// function calls need their parameters to be copy constructed
+			static void insert( std::list<ast::ptr<ast::Decl> > & translationUnit );
+
+			const ast::Expr * postvisit( const ast::ApplicationExpr * appExpr );
+
+			// only handles each UniqueExpr once
+			// if order of visit does not change, this should be safe
+			void previsit (const ast::UniqueExpr *);
+
+			std::unordered_set<decltype(ast::UniqueExpr::id)> visitedIds;
+		};
+
+		struct ResolveCopyCtors final : public ast::WithGuards, public ast::WithStmtsToAdd<>, public ast::WithSymbolTable, public ast::WithShortCircuiting, public ast::WithVisitorRef<ResolveCopyCtors> {
+			/// generate temporary ObjectDecls for each argument and return value of each ImplicitCopyCtorExpr,
+			/// generate/resolve copy construction expressions for each, and generate/resolve destructors for both
+			/// arguments and return value temporaries
+			static void resolveImplicitCalls( std::list<ast::ptr<ast::Decl> > & translationUnit );
+
+			const ast::Expr * postvisit( const ast::ImplicitCopyCtorExpr * impCpCtorExpr );
+			const ast::StmtExpr * previsit( const ast::StmtExpr * stmtExpr );
+			const ast::UniqueExpr * previsit( const ast::UniqueExpr * unqExpr );
+
+			/// handles distant mutations of environment manually. 
+			/// WithConstTypeSubstitution cannot remember where the environment is from
+
+			/// MUST be called at start of overload previsit
+			void previsit( const ast::Expr * expr);
+			/// MUST be called at return of overload postvisit
+			const ast::Expr * postvisit(const ast::Expr * expr);
+
+			/// create and resolve ctor/dtor expression: fname(var, [cpArg])
+			const ast::Expr * makeCtorDtor( const std::string & fname, const ast::ObjectDecl * var, const ast::Expr * cpArg = nullptr );
+			/// true if type does not need to be copy constructed to ensure correctness
+			bool skipCopyConstruct( const ast::Type * type );
+			ast::ptr< ast::Expr > copyConstructArg( const ast::Expr * arg, const ast::ImplicitCopyCtorExpr * impCpCtorExpr, const ast::Type * formal );
+			ast::Expr * destructRet( const ast::ObjectDecl * ret, const ast::Expr * arg );
+		private:
+			/// hack to implement WithTypeSubstitution while conforming to mutation safety.
+			ast::TypeSubstitution * env;
+			bool                    envModified;
+		};
+
+		/// collects constructed object decls - used as a base class
+		struct ObjDeclCollector : public ast::WithGuards, public ast::WithShortCircuiting {
+			// use ordered data structure to maintain ordering for set_difference and for consistent error messages
+			typedef std::list< const ast::ObjectDecl * > ObjectSet;
+			void previsit( const ast::CompoundStmt *compoundStmt );
+			void previsit( const ast::DeclStmt *stmt );
+
+			// don't go into other functions
+			void previsit( const ast::FunctionDecl * ) { visit_children = false; }
+
+		  protected:
+			ObjectSet curVars;
+		};
+
+		// debug
+		template<typename ObjectSet>
+		struct PrintSet {
+			PrintSet( const ObjectSet & objs ) : objs( objs ) {}
+			const ObjectSet & objs;
+		};
+		template<typename ObjectSet>
+		PrintSet<ObjectSet> printSet( const ObjectSet & objs ) { return PrintSet<ObjectSet>( objs ); }
+		template<typename ObjectSet>
+		std::ostream & operator<<( std::ostream & out, const PrintSet<ObjectSet> & set) {
+			out << "{ ";
+			for ( auto & obj : set.objs ) {
+				out << obj->name << ", " ;
+			} // for
+			out << " }";
+			return out;
+		}
+
+		struct LabelFinder final : public ObjDeclCollector {
+			typedef std::map< std::string, ObjectSet > LabelMap;
+			// map of Label -> live variables at that label
+			LabelMap vars;
+
+			typedef ObjDeclCollector Parent;
+			using Parent::previsit;
+			void previsit( const ast::Stmt * stmt );
+
+			void previsit( const ast::CompoundStmt *compoundStmt );
+			void previsit( const ast::DeclStmt *stmt );
+		};
+
+		struct InsertDtors final : public ObjDeclCollector, public ast::WithStmtsToAdd<> {
+			/// insert destructor calls at the appropriate places.  must happen before CtorInit nodes are removed
+			/// (currently by FixInit)
+			static void insert( std::list< ast::ptr<ast::Decl> > & translationUnit );
+
+			typedef std::list< ObjectDecl * > OrderedDecls;
+			typedef std::list< OrderedDecls > OrderedDeclsStack;
+
+			InsertDtors( ast::Pass<LabelFinder> & finder ) : finder( finder ), labelVars( finder.core.vars ) {}
+
+			typedef ObjDeclCollector Parent;
+			using Parent::previsit;
+
+			void previsit( const ast::FunctionDecl * funcDecl );
+
+			void previsit( const ast::BranchStmt * stmt );
+		private:
+			void handleGoto( const ast::BranchStmt * stmt );
+
+			ast::Pass<LabelFinder> & finder;
+			LabelFinder::LabelMap & labelVars;
+			OrderedDeclsStack reverseDeclOrder;
+		};
+
+		class FixInit : public ast::WithStmtsToAdd<> {
+		  public:
+			/// expand each object declaration to use its constructor after it is declared.
+			static void fixInitializers( std::list< ast::ptr<ast::Decl> > &translationUnit );
+
+			const ast::DeclWithType * postvisit( const ast::ObjectDecl *objDecl );
+
+			std::list< ast::ptr< ast::Decl > > staticDtorDecls;
+		};
+
+		struct GenStructMemberCalls final : public ast::WithGuards, public ast::WithShortCircuiting, public ast::WithSymbolTable, public ast::WithVisitorRef<GenStructMemberCalls> {
+			/// generate default/copy ctor and dtor calls for user-defined struct ctor/dtors
+			/// for any member that is missing a corresponding ctor/dtor call.
+			/// error if a member is used before constructed
+			static void generate( std::list< ast::ptr<ast::Decl> > & translationUnit );
+
+			void previsit( const ast::FunctionDecl * funcDecl );
+			const ast::DeclWithType * postvisit( const ast::FunctionDecl * funcDecl );
+
+			void previsit( const ast::MemberExpr * memberExpr );
+			void previsit( const ast::ApplicationExpr * appExpr );
+
+			/// Note: this post mutate used to be in a separate visitor. If this pass breaks, one place to examine is whether it is
+			/// okay for this part of the recursion to occur alongside the rest.
+			const ast::Expr * postvisit( const ast::UntypedExpr * expr );
+
+			SemanticErrorException errors;
+		  private:
+			template< typename... Params >
+			void emit( CodeLocation, const Params &... params );
+
+			ast::FunctionDecl * function = nullptr;
+			std::set< const ast::DeclWithType * > unhandled;
+			std::map< const ast::DeclWithType *, CodeLocation > usedUninit;
+			const ast::ObjectDecl * thisParam = nullptr;
+			bool isCtor = false; // true if current function is a constructor
+			const ast::StructDecl * structDecl = nullptr;
+		};
+
+		struct FixCtorExprs final : public ast::WithDeclsToAdd<>, public ast::WithSymbolTable, public ast::WithShortCircuiting {
+			/// expands ConstructorExpr nodes into comma expressions, using a temporary for the first argument
+			static void fix( std::list< ast::ptr<ast::Decl> > & translationUnit );
+
+			const ast::Expr * postvisit( const ast::ConstructorExpr * ctorExpr );
+		};
+
+		struct SplitExpressions : public ast::WithShortCircuiting {
+			/// add CompoundStmts around top-level expressions so that temporaries are destroyed in the correct places.
+			static void split( std::list<ast::ptr<ast::Decl> > & translationUnit );
+
+			ast::Stmt * postvisit( const ast::ExprStmt * stmt );
+			void previsit( const ast::TupleAssignExpr * expr );
+		};
+	} // namespace
+
+	void fix( std::list< ast::ptr<ast::Decl> > & translationUnit, bool inLibrary ) {
+		ast::Pass<SelfAssignChecker> checker;
+		accept_all( translationUnit, checker );
+
+		// fixes StmtExpr to properly link to their resulting expression
+		StmtExprResult::link( translationUnit );
+
+		// fixes ConstructorInit for global variables. should happen before fixInitializers.
+		InitTweak::fixGlobalInit( translationUnit, inLibrary );
+
+		// must happen before ResolveCopyCtors because temporaries have to be inserted into the correct scope
+		SplitExpressions::split( translationUnit );
+
+		InsertImplicitCalls::insert( translationUnit );
+
+		// Needs to happen before ResolveCopyCtors, because argument/return temporaries should not be considered in
+		// error checking branch statements
+		InsertDtors::insert( translationUnit );
+
+		ResolveCopyCtors::resolveImplicitCalls( translationUnit );
+		FixInit::fixInitializers( translationUnit );
+		GenStructMemberCalls::generate( translationUnit );
+
+		// Needs to happen after GenStructMemberCalls, since otherwise member constructors exprs
+		// don't have the correct form, and a member can be constructed more than once.
+		FixCtorExprs::fix( translationUnit );
+	}
+
+	namespace {
+		/// find and return the destructor used in `input`. If `input` is not a simple destructor call, generate a thunk
+		/// that wraps the destructor, insert it into `stmtsToAdd` and return the new function declaration
+		const ast::DeclWithType * getDtorFunc( const ast::ObjectDecl * objDecl, const ast::Stmt * input, std::list< ast::ptr<ast::Stmt> > & stmtsToAdd ) {
+			const CodeLocation loc = input->location;
+			// unwrap implicit statement wrapper
+			// Statement * dtor = input;
+			assert( input );
+			// std::list< const ast::Expr * > matches;
+			auto matches = collectCtorDtorCalls( input );
+
+			if ( dynamic_cast< const ast::ExprStmt * >( input ) ) {
+				// only one destructor call in the expression
+				if ( matches.size() == 1 ) {
+					auto func = getFunction( matches.front() );
+					assertf( func, "getFunction failed to find function in %s", toString( matches.front() ).c_str() );
+
+					// cleanup argument must be a function, not an object (including function pointer)
+					if ( auto dtorFunc = dynamic_cast< const ast::FunctionDecl * > ( func ) ) {
+						if ( dtorFunc->type->forall.empty() ) {
+							// simple case where the destructor is a monomorphic function call - can simply
+							// use that function as the cleanup function.
+							return func;
+						}
+					}
+				}
+			}
+
+			// otherwise the cleanup is more complicated - need to build a single argument cleanup function that
+			// wraps the more complicated code.
+			static UniqueName dtorNamer( "__cleanup_dtor" );
+			std::string name = dtorNamer.newName();
+			ast::FunctionDecl * dtorFunc = SymTab::genDefaultFunc( loc, name, objDecl->type->stripReferences(), false );
+			stmtsToAdd.push_back( new ast::DeclStmt(loc, dtorFunc ) );
+
+			// the original code contains uses of objDecl - replace them with the newly generated 'this' parameter.
+			const ast::ObjectDecl * thisParam = getParamThis( dtorFunc );
+			const ast::Expr * replacement = new ast::VariableExpr( loc, thisParam );
+
+			auto base = replacement->result->stripReferences();
+			if ( dynamic_cast< const ast::ArrayType * >( base ) || dynamic_cast< const ast::TupleType * > ( base ) ) {
+				// need to cast away reference for array types, since the destructor is generated without the reference type,
+				// and for tuple types since tuple indexing does not work directly on a reference
+				replacement = new ast::CastExpr( replacement, base );
+			}
+			auto dtor = ast::DeclReplacer::replace( input, ast::DeclReplacer::ExprMap{ std::make_pair( objDecl, replacement ) } );
+			auto mutStmts = dtorFunc->stmts.get_and_mutate();
+			mutStmts->push_back(strict_dynamic_cast<const ast::Stmt *>( dtor ));
+			dtorFunc->stmts = mutStmts;
+
+			return dtorFunc;
+		}
+
+		void StmtExprResult::link( std::list<ast::ptr<ast::Decl> > & translationUnit ) {
+			ast::Pass<StmtExprResult> linker;
+			accept_all( translationUnit, linker );
+		}
+
+		void SplitExpressions::split( std::list<ast::ptr<ast::Decl> > & translationUnit ) {
+			ast::Pass<SplitExpressions> splitter;
+			accept_all( translationUnit, splitter );
+		}
+
+		void InsertImplicitCalls::insert( std::list<ast::ptr<ast::Decl> > & translationUnit ) {
+			ast::Pass<InsertImplicitCalls> inserter;
+			accept_all( translationUnit, inserter );
+		}
+
+		void ResolveCopyCtors::resolveImplicitCalls( std::list< ast::ptr<ast::Decl> > & translationUnit ) {
+			ast::Pass<ResolveCopyCtors> resolver;
+			accept_all( translationUnit, resolver );
+		}
+
+		void FixInit::fixInitializers( std::list< ast::ptr<ast::Decl> > & translationUnit ) {
+			ast::Pass<FixInit> fixer;
+
+			// can't use mutateAll, because need to insert declarations at top-level
+			// can't use DeclMutator, because sometimes need to insert IfStmt, etc.
+			SemanticErrorException errors;
+			for ( auto i = translationUnit.begin(); i != translationUnit.end(); ++i ) {
+				try {
+					// maybeAccept( *i, fixer ); translationUnit should never contain null
+					*i = (*i)->accept(fixer);
+					translationUnit.splice( i, fixer.core.staticDtorDecls );
+				} catch( SemanticErrorException &e ) {
+					errors.append( e );
+				} // try
+			} // for
+			if ( ! errors.isEmpty() ) {
+				throw errors;
+			} // if
+		}
+
+		void InsertDtors::insert( std::list< ast::ptr<ast::Decl> > & translationUnit ) {
+			ast::Pass<LabelFinder> finder;
+			ast::Pass<InsertDtors> inserter( finder );
+			accept_all( translationUnit, inserter );
+		}
+
+		void GenStructMemberCalls::generate( std::list< ast::ptr<ast::Decl> > & translationUnit ) {
+			ast::Pass<GenStructMemberCalls> warner;
+			accept_all( translationUnit, warner );
+		}
+
+		void FixCtorExprs::fix( std::list< ast::ptr<ast::Decl> > & translationUnit ) {
+			ast::Pass<FixCtorExprs> fixer;
+			accept_all( translationUnit, fixer );
+		}
+
+		const ast::StmtExpr * StmtExprResult::previsit( const ast::StmtExpr * stmtExpr ) {
+			// we might loose the result expression here so add a pointer to trace back
+			assert( stmtExpr->result );
+			const ast::Type * result = stmtExpr->result;
+			if ( ! result->isVoid() ) {
+				auto mutExpr = mutate(stmtExpr);
+				const ast::CompoundStmt * body = mutExpr->stmts;
+				assert( ! body->kids.empty() );
+				mutExpr->resultExpr = body->kids.back().strict_as<ast::ExprStmt>();
+				return mutExpr;
+			}
+			return stmtExpr;
+		}
+
+		ast::Stmt * SplitExpressions::postvisit( const ast::ExprStmt * stmt ) {
+			// wrap each top-level ExprStmt in a block so that destructors for argument and return temporaries are destroyed
+			// in the correct places
+			ast::CompoundStmt * ret = new ast::CompoundStmt( stmt->location, { stmt } );
+			return ret;
+		}
+
+		void SplitExpressions::previsit( const ast::TupleAssignExpr * ) {
+			// don't do this within TupleAssignExpr, since it is already broken up into multiple expressions
+			visit_children = false;
+		}
+
+		// Relatively simple structural comparison for expressions, needed to determine
+		// if two expressions are "the same" (used to determine if self assignment occurs)
+		struct StructuralChecker {
+			const ast::Expr * stripCasts( const ast::Expr * expr ) {
+				// this might be too permissive. It's possible that only particular casts are relevant.
+				while ( auto cast = dynamic_cast< const ast::CastExpr * >( expr ) ) {
+					expr = cast->arg;
+				}
+				return expr;
+			}
+
+			void previsit( const ast::Expr * ) {
+				// anything else does not qualify
+				isSimilar = false;
+			}
+
+			template<typename T>
+			T * cast( const ast::Expr * node ) {
+				// all expressions need to ignore casts, so this bit has been factored out
+				return dynamic_cast< T * >( stripCasts( node ) );
+			}
+
+			// ignore casts
+			void previsit( const ast::CastExpr * ) {}
+
+			void previsit( const ast::MemberExpr * memExpr ) {
+				if ( auto otherMember = cast< const ast::MemberExpr >( other ) ) {
+					if ( otherMember->member == memExpr->member ) {
+						other = otherMember->aggregate;
+						return;
+					}
+				}
+				isSimilar = false;
+			}
+
+			void previsit( const ast::VariableExpr * varExpr ) {
+				if ( auto otherVar = cast< const ast::VariableExpr >( other ) ) {
+					if ( otherVar->var == varExpr->var ) {
+						return;
+					}
+				}
+				isSimilar = false;
+			}
+
+			void previsit( const ast::AddressExpr * ) {
+				if ( auto addrExpr = cast< const ast::AddressExpr >( other ) ) {
+					other = addrExpr->arg;
+					return;
+				}
+				isSimilar = false;
+			}
+
+			const ast::Expr * other = nullptr;
+			bool isSimilar = true;
+		};
+
+		bool structurallySimilar( const ast::Expr * e1, const ast::Expr * e2 ) {
+			ast::Pass<StructuralChecker> checker;
+			checker.core.other = e2;
+			e1->accept( checker );
+			return checker.core.isSimilar;
+		}
+
+		void SelfAssignChecker::previsit( const ast::ApplicationExpr * appExpr ) {
+			auto function = getFunction( appExpr );
+			if ( function->name == "?=?" ) { // doesn't use isAssignment, because ?+=?, etc. should not count as self-assignment
+				if ( appExpr->args.size() == 2 ) {
+					// check for structural similarity (same variable use, ignore casts, etc. - but does not look too deeply, anything looking like a function is off limits)
+					if ( structurallySimilar( appExpr->args.front(), appExpr->args.back() ) ) {
+						SemanticWarning( appExpr->location, Warning::SelfAssignment, toCString( appExpr->args.front() ) );
+					}
+				}
+			}
+		}
+
+		const ast::Expr * InsertImplicitCalls::postvisit( const ast::ApplicationExpr * appExpr ) {
+			if ( auto function = appExpr->func.as<ast::VariableExpr>() ) {
+				if ( function->var->linkage.is_builtin ) {
+					// optimization: don't need to copy construct in order to call intrinsic functions
+					return appExpr;
+				} else if ( auto funcDecl = function->var.as<ast::DeclWithType>() ) {
+					auto ftype = dynamic_cast< const ast::FunctionType * >( GenPoly::getFunctionType( funcDecl->get_type() ) );
+					assertf( ftype, "Function call without function type: %s", toString( funcDecl ).c_str() );
+					if ( CodeGen::isConstructor( funcDecl->name ) && ftype->params.size() == 2 ) {
+						auto t1 = getPointerBase( ftype->params.front() );
+						auto t2 = ftype->params.back();
+						assert( t1 );
+
+						if ( ResolvExpr::typesCompatible( t1, t2 ) ) {
+							// optimization: don't need to copy construct in order to call a copy constructor
+							return appExpr;
+						} // if
+					} else if ( CodeGen::isDestructor( funcDecl->name ) ) {
+						// correctness: never copy construct arguments to a destructor
+						return appExpr;
+					} // if
+				} // if
+			} // if
+			CP_CTOR_PRINT( std::cerr << "InsertImplicitCalls: adding a wrapper " << appExpr << std::endl; )
+
+			// wrap each function call so that it is easy to identify nodes that have to be copy constructed
+			ast::ptr<ast::TypeSubstitution> tmp = appExpr->env;
+			auto mutExpr = mutate(appExpr);
+			mutExpr->env = nullptr;
+
+			auto expr = new ast::ImplicitCopyCtorExpr( appExpr->location, mutExpr );
+			// Move the type substitution to the new top-level, if it is attached to the appExpr.
+			// Ensure it is not deleted with the ImplicitCopyCtorExpr by removing it before deletion.
+			// The substitution is needed to obtain the type of temporary variables so that copy constructor
+			// calls can be resolved.
+			assert( typeSubs );
+			// assert (mutExpr->env);
+			expr->env = tmp;
+			// mutExpr->env = nullptr;
+			//std::swap( expr->env, appExpr->env );
+			return expr;
+		}
+
+		void ResolveCopyCtors::previsit(const ast::Expr * expr) {
+			if (expr->env) {
+				GuardValue(env);
+				GuardValue(envModified);
+				env = expr->env->clone();
+				envModified = false;
+			}
+		}
+
+		const ast::Expr * ResolveCopyCtors::postvisit(const ast::Expr * expr) {
+			if (expr->env) {
+				if (envModified) {
+					auto mutExpr = mutate(expr);
+					mutExpr->env = env;
+					return mutExpr;
+				}
+				else {
+					// env was not mutated, skip and delete the shallow copy
+					delete env;
+					return expr;
+				}
+			}
+			else {
+				return expr;
+			}
+		}
+
+		bool ResolveCopyCtors::skipCopyConstruct( const ast::Type * type ) { return ! isConstructable( type ); }
+
+		const ast::Expr * ResolveCopyCtors::makeCtorDtor( const std::string & fname, const ast::ObjectDecl * var, const ast::Expr * cpArg ) {
+			assert( var );
+			assert (var->isManaged());
+			assert (!cpArg || cpArg->isManaged());
+			// arrays are not copy constructed, so this should always be an ExprStmt
+			ast::ptr< ast::Stmt > stmt = genCtorDtor(var->location, fname, var, cpArg );
+			assertf( stmt, "ResolveCopyCtors: genCtorDtor returned nullptr: %s / %s / %s", fname.c_str(), toString( var ).c_str(), toString( cpArg ).c_str() );
+			auto exprStmt = stmt.strict_as<ast::ImplicitCtorDtorStmt>()->callStmt.strict_as<ast::ExprStmt>();
+			ast::ptr<ast::Expr> untyped = exprStmt->expr; // take ownership of expr
+			// exprStmt->expr = nullptr; 
+
+			// resolve copy constructor
+			// should only be one alternative for copy ctor and dtor expressions, since all arguments are fixed
+			// (VariableExpr and already resolved expression)
+			CP_CTOR_PRINT( std::cerr << "ResolvingCtorDtor " << untyped << std::endl; )
+			ast::ptr<ast::Expr> resolved = ResolvExpr::findVoidExpression(untyped, symtab);
+			assert( resolved );
+			if ( resolved->env ) {
+				// Extract useful information and discard new environments. Keeping them causes problems in PolyMutator passes.
+				env->add( *resolved->env );
+				envModified = true;
+				// delete resolved->env;
+				auto mut = mutate(resolved.get());
+				assertf(mut == resolved.get(), "newly resolved expression must be unique");
+				mut->env = nullptr;
+			} // if
+			// delete stmt;
+			if ( auto assign = resolved.as<ast::TupleAssignExpr>() ) {
+				// fix newly generated StmtExpr
+				previsit( assign->stmtExpr );
+			}
+			return resolved.release();
+		}
+
+		ast::ptr<ast::Expr> ResolveCopyCtors::copyConstructArg( 
+			const ast::Expr * arg, const ast::ImplicitCopyCtorExpr * impCpCtorExpr, const ast::Type * formal )
+		{
+			static UniqueName tempNamer("_tmp_cp");
+			assert( env );
+			const CodeLocation loc = impCpCtorExpr->location;
+			// CP_CTOR_PRINT( std::cerr << "Type Substitution: " << *env << std::endl; )
+			assert( arg->result );
+			ast::ptr<ast::Type> result = arg->result;
+			if ( skipCopyConstruct( result ) ) return arg; // skip certain non-copyable types
+
+			// type may involve type variables, so apply type substitution to get temporary variable's actual type,
+			// since result type may not be substituted (e.g., if the type does not appear in the parameter list)
+			// Use applyFree so that types bound in function pointers are not substituted, e.g. in forall(dtype T) void (*)(T).
+			
+			// xxx - this originally mutates arg->result in place. is it correct?
+			result = env->applyFree( result.get() ).node;
+			auto mutResult = result.get_and_mutate();
+			mutResult->set_const(false);
+
+			auto mutArg = mutate(arg);
+			mutArg->result = mutResult;
+
+			ast::ptr<ast::Expr> guard = mutArg;
+
+			ast::ptr<ast::ObjectDecl> tmp = new ast::ObjectDecl({}, "__tmp", mutResult, nullptr );
+
+			// create and resolve copy constructor
+			CP_CTOR_PRINT( std::cerr << "makeCtorDtor for an argument" << std::endl; )
+			auto cpCtor = makeCtorDtor( "?{}", tmp, mutArg );
+
+			if ( auto appExpr = dynamic_cast< const ast::ApplicationExpr * >( cpCtor ) ) {
+				// if the chosen constructor is intrinsic, the copy is unnecessary, so
+				// don't create the temporary and don't call the copy constructor
+				auto function = appExpr->func.strict_as<ast::VariableExpr>();
+				if ( function->var->linkage == ast::Linkage::Intrinsic ) {
+					// arguments that need to be boxed need a temporary regardless of whether the copy constructor is intrinsic,
+					// so that the object isn't changed inside of the polymorphic function
+					if ( ! GenPoly::needsBoxing( formal, result, impCpCtorExpr->callExpr, env ) ) {
+						// xxx - should arg->result be mutated? see comment above.
+						return guard;
+					}
+				}
+			}
+
+			// set a unique name for the temporary once it's certain the call is necessary
+			auto mut = tmp.get_and_mutate();
+			assertf (mut == tmp, "newly created ObjectDecl must be unique");
+			mut->name = tempNamer.newName();
+
+			// replace argument to function call with temporary
+			stmtsToAddBefore.push_back( new ast::DeclStmt(loc, tmp ) );
+			arg = cpCtor;
+			return destructRet( tmp, arg );
+
+			// impCpCtorExpr->dtors.push_front( makeCtorDtor( "^?{}", tmp ) );
+		}
+
+		ast::Expr * ResolveCopyCtors::destructRet( const ast::ObjectDecl * ret, const ast::Expr * arg ) {
+			// TODO: refactor code for generating cleanup attribute, since it's common and reused in ~3-4 places
+			// check for existing cleanup attribute before adding another(?)
+			// need to add __Destructor for _tmp_cp variables as well
+
+			assertf( ast::dtorStruct && ast::dtorStruct->members.size() == 2, "Destructor generation requires __Destructor definition." );
+			assertf( ast::dtorStructDestroy, "Destructor generation requires __destroy_Destructor." );
+
+			const CodeLocation loc = ret->location;
+
+			// generate a __Destructor for ret that calls the destructor
+			auto res = makeCtorDtor( "^?{}", ret );
+			auto dtor = mutate(res);
+
+			// if the chosen destructor is intrinsic, elide the generated dtor handler
+			if ( arg && isIntrinsicCallExpr( dtor ) ) {
+				return new ast::CommaExpr(loc, arg, new ast::VariableExpr(loc, ret ) );
+				// return;
+			}
+
+			if ( ! dtor->env ) dtor->env = maybeClone( env );
+			auto dtorFunc = getDtorFunc( ret, new ast::ExprStmt(loc, dtor ), stmtsToAddBefore );
+
+			auto dtorStructType = new ast::StructInstType(ast::dtorStruct);
+
+			// what does this do???
+			dtorStructType->params.push_back( new ast::TypeExpr(loc, new ast::VoidType() ) );
+
+			// cast destructor pointer to void (*)(void *), to silence GCC incompatible pointer warnings
+			auto dtorFtype = new ast::FunctionType();
+			dtorFtype->params.push_back( new ast::PointerType(new ast::VoidType( ) ) );
+			auto dtorType = new ast::PointerType( dtorFtype );
+
+			static UniqueName namer( "_ret_dtor" );
+			auto retDtor = new ast::ObjectDecl(loc, namer.newName(), dtorStructType, new ast::ListInit(loc, { new ast::SingleInit(loc, ast::ConstantExpr::null(loc) ), new ast::SingleInit(loc, new ast::CastExpr( new ast::VariableExpr(loc, dtorFunc ), dtorType ) ) } ) );
+			retDtor->attributes.push_back( new ast::Attribute( "cleanup", { new ast::VariableExpr(loc, ast::dtorStructDestroy ) } ) );
+			stmtsToAddBefore.push_back( new ast::DeclStmt(loc, retDtor ) );
+
+			if ( arg ) {
+				auto member = new ast::MemberExpr(loc, ast::dtorStruct->members.front().strict_as<ast::DeclWithType>(), new ast::VariableExpr(loc, retDtor ) );
+				auto object = new ast::CastExpr( new ast::AddressExpr( new ast::VariableExpr(loc, ret ) ), new ast::PointerType(new ast::VoidType() ) );
+				ast::Expr * assign = createBitwiseAssignment( member, object );
+				return new ast::CommaExpr(loc, new ast::CommaExpr(loc, arg, assign ), new ast::VariableExpr(loc, ret ) );
+			}
+			return nullptr;
+			// impCpCtorExpr->get_dtors().push_front( makeCtorDtor( "^?{}", ret ) );
+		}
+
+		const ast::Expr * ResolveCopyCtors::postvisit( const ast::ImplicitCopyCtorExpr *impCpCtorExpr ) {
+			CP_CTOR_PRINT( std::cerr << "ResolveCopyCtors: " << impCpCtorExpr << std::endl; )
+
+			ast::ApplicationExpr * appExpr = mutate(impCpCtorExpr->callExpr.get());
+			const ast::ObjectDecl * returnDecl = nullptr;
+			const CodeLocation loc = appExpr->location;
+
+			// take each argument and attempt to copy construct it.
+			auto ftype = GenPoly::getFunctionType( appExpr->func->result );
+			assert( ftype );
+			auto & params = ftype->params;
+			auto iter = params.begin();
+			for ( auto & arg : appExpr->args ) {
+				const ast::Type * formal = nullptr;
+				if ( iter != params.end() ) { // does not copy construct C-style variadic arguments
+					// DeclarationWithType * param = *iter++;
+					formal = *iter++;
+				}
+
+				arg = copyConstructArg( arg, impCpCtorExpr, formal );
+			} // for
+
+			// each return value from the call needs to be connected with an ObjectDecl at the call site, which is
+			// initialized with the return value and is destructed later
+			// xxx - handle named return values?
+			const ast::Type * result = appExpr->result;
+			if ( ! result->isVoid() ) {
+				static UniqueName retNamer("_tmp_cp_ret");
+				// result = result->clone();
+				auto subResult = env->apply( result ).node;
+				auto ret = new ast::ObjectDecl(loc, retNamer.newName(), subResult, nullptr );
+				auto mutType = mutate(ret->type.get());
+				mutType->set_const( false );
+				ret->type = mutType;
+				returnDecl = ret;
+				stmtsToAddBefore.push_back( new ast::DeclStmt(loc, ret ) );
+				CP_CTOR_PRINT( std::cerr << "makeCtorDtor for a return" << std::endl; )
+			} // for
+			CP_CTOR_PRINT( std::cerr << "after Resolving: " << impCpCtorExpr << std::endl; )
+			// ------------------------------------------------------
+
+			CP_CTOR_PRINT( std::cerr << "Coming out the back..." << impCpCtorExpr << std::endl; )
+
+			// detach fields from wrapper node so that it can be deleted without deleting too much
+
+			// xxx - actual env might be somewhere else, need to keep invariant
+
+			// deletion of wrapper should be handled by pass template now
+			
+			// impCpCtorExpr->callExpr = nullptr;
+			assert (appExpr->env == nullptr);
+			appExpr->env = impCpCtorExpr->env;
+			// std::swap( impCpCtorExpr->env, appExpr->env );
+			// assert( impCpCtorExpr->env == nullptr );
+			// delete impCpCtorExpr;
+
+			if ( returnDecl ) {
+				ast::Expr * assign = createBitwiseAssignment( new ast::VariableExpr(loc, returnDecl ), appExpr );
+				if ( ! dynamic_cast< const ast::ReferenceType * >( result ) ) {
+					// destructing reference returns is bad because it can cause multiple destructor calls to the same object - the returned object is not a temporary
+					assign = destructRet( returnDecl, assign );
+					assert(assign);
+				} else {
+					assign = new ast::CommaExpr(loc, assign, new ast::VariableExpr(loc, returnDecl ) );
+				}
+				// move env from appExpr to retExpr
+				// std::swap( assign->env, appExpr->env );
+				assign->env = appExpr->env;
+				// actual env is handled by common routine that replaces WithTypeSubstitution
+				return postvisit((const ast::Expr *)assign);
+			} else {
+				return postvisit((const ast::Expr *)appExpr);
+			} // if
+		}
+
+		const ast::StmtExpr * ResolveCopyCtors::previsit( const ast::StmtExpr * _stmtExpr ) {
+			// function call temporaries should be placed at statement-level, rather than nested inside of a new statement expression,
+			// since temporaries can be shared across sub-expressions, e.g.
+			//   [A, A] f();       // decl
+			//   g([A] x, [A] y);  // decl
+			//   g(f());           // call
+			// f is executed once, so the return temporary is shared across the tuple constructors for x and y.
+			// Explicitly mutating children instead of mutating the inner compound statement forces the temporaries to be added
+			// to the outer context, rather than inside of the statement expression.
+
+			// call the common routine that replaces WithTypeSubstitution
+			previsit((const ast::Expr *) _stmtExpr);
+
+			visit_children = false;
+			const CodeLocation loc = _stmtExpr->location;
+
+			assert( env );
+
+			symtab.enterScope();
+			// visit all statements
+			auto stmtExpr = mutate(_stmtExpr);
+			auto mutStmts = mutate(stmtExpr->stmts.get());
+
+			auto & stmts = mutStmts->kids;
+			for ( auto & stmt : stmts ) {
+				stmt = stmt->accept( *visitor );
+			} // for
+			stmtExpr->stmts = mutStmts;
+			symtab.leaveScope();
+
+			assert( stmtExpr->result );
+			// const ast::Type * result = stmtExpr->result;
+			if ( ! stmtExpr->result->isVoid() ) {
+				static UniqueName retNamer("_tmp_stmtexpr_ret");
+
+				// result = result->clone();
+				auto result = env->apply( stmtExpr->result.get() ).node;
+				if ( ! InitTweak::isConstructable( result ) ) {
+					// delete result;
+					return stmtExpr;
+				}
+				auto mutResult = result.get_and_mutate();
+				mutResult->set_const(false);
+
+				// create variable that will hold the result of the stmt expr
+				auto ret = new ast::ObjectDecl(loc, retNamer.newName(), mutResult, nullptr );
+				stmtsToAddBefore.push_back( new ast::DeclStmt(loc, ret ) );
+
+				assertf(
+					stmtExpr->resultExpr,
+					"Statement-Expression should have a resulting expression at %s:%d",
+					stmtExpr->location.filename.c_str(),
+					stmtExpr->location.first_line
+				);
+
+				const ast::ExprStmt * last = stmtExpr->resultExpr;
+				// xxx - if this is non-unique, need to copy while making resultExpr ref
+				assertf(last->unique(), "attempt to modify weakly shared statement");
+				auto mutLast = mutate(last);
+				// above assertion means in-place mutation is OK
+				try {
+					mutLast->expr = makeCtorDtor( "?{}", ret, mutLast->expr );
+				} catch(...) {
+					std::cerr << "*CFA internal error: ";
+					std::cerr << "can't resolve implicit constructor";
+					std::cerr << " at " << stmtExpr->location.filename;
+					std::cerr << ":" << stmtExpr->location.first_line << std::endl;
+
+					abort();
+				}
+
+				// add destructors after current statement
+				stmtsToAddAfter.push_back( new ast::ExprStmt(loc, makeCtorDtor( "^?{}", ret ) ) );
+
+				// must have a non-empty body, otherwise it wouldn't have a result
+				assert( ! stmts.empty() );
+
+				// if there is a return decl, add a use as the last statement; will not have return decl on non-constructable returns
+				stmts.push_back( new ast::ExprStmt(loc, new ast::VariableExpr(loc, ret ) ) );
+			} // if
+
+			assert( stmtExpr->returnDecls.empty() );
+			assert( stmtExpr->dtors.empty() );
+
+			return stmtExpr;
+		}
+
+		// to prevent warnings ('_unq0' may be used uninitialized in this function),
+		// insert an appropriate zero initializer for UniqueExpr temporaries.
+		ast::Init * makeInit( const ast::Type * t ) {
+			if ( auto inst = dynamic_cast< const ast::StructInstType * >( t ) ) {
+				// initizer for empty struct must be empty
+				if ( inst->base->members.empty() ) return new ast::ListInit({}, {});
+			} else if ( auto inst = dynamic_cast< const ast::UnionInstType * >( t ) ) {
+				// initizer for empty union must be empty
+				if ( inst->base->members.empty() ) return new ast::ListInit({}, {});
+			}
+
+			return new ast::ListInit( {}, { new ast::SingleInit( {}, ast::ConstantExpr::from_int({}, 0) ) } );
+		}
+
+		const ast::UniqueExpr * ResolveCopyCtors::previsit( const ast::UniqueExpr * unqExpr ) {
+			visit_children = false;
+			// xxx - hack to prevent double-handling of unique exprs, otherwise too many temporary variables and destructors are generated
+			static std::unordered_map< int, const ast::UniqueExpr * > unqMap;
+			auto mutExpr = mutate(unqExpr);
+			if ( ! unqMap.count( unqExpr->id ) ) {
+				// resolve expr and find its
+
+				auto impCpCtorExpr = mutExpr->expr.as<ast::ImplicitCopyCtorExpr>();
+				// PassVisitor<ResolveCopyCtors> fixer;
+				
+				mutExpr->expr = mutExpr->expr->accept( *visitor );
+				// it should never be necessary to wrap a void-returning expression in a UniqueExpr - if this assumption changes, this needs to be rethought
+				assert( unqExpr->result );
+				if ( impCpCtorExpr ) {
+					auto comma = unqExpr->expr.strict_as<ast::CommaExpr>();
+					auto var = comma->arg2.strict_as<ast::VariableExpr>();
+					// note the variable used as the result from the call
+					mutExpr->var = var;
+				} else {
+					// expr isn't a call expr, so create a new temporary variable to use to hold the value of the unique expression
+					mutExpr->object = new ast::ObjectDecl( mutExpr->location, toString("_unq", mutExpr->id), mutExpr->result, makeInit( mutExpr->result ) );
+					mutExpr->var = new ast::VariableExpr( mutExpr->location, mutExpr->object );
+				}
+
+				// stmtsToAddBefore.splice( stmtsToAddBefore.end(), fixer.pass.stmtsToAddBefore );
+				// stmtsToAddAfter.splice( stmtsToAddAfter.end(), fixer.pass.stmtsToAddAfter );
+				unqMap[mutExpr->id] = mutExpr;
+			} else {
+				// take data from other UniqueExpr to ensure consistency
+				// delete unqExpr->get_expr();
+				mutExpr->expr = unqMap[mutExpr->id]->expr;
+				// delete unqExpr->result;
+				mutExpr->result = mutExpr->expr->result;
+			}
+			return mutExpr;
+		}
+
+		const ast::DeclWithType * FixInit::postvisit( const ast::ObjectDecl *_objDecl ) {
+			const CodeLocation loc = _objDecl->location;
+
+			// since this removes the init field from objDecl, it must occur after children are mutated (i.e. postvisit)
+			if ( ast::ptr<ast::ConstructorInit> ctorInit = _objDecl->init.as<ast::ConstructorInit>() ) {
+				auto objDecl = mutate(_objDecl);
+
+				// could this be non-unique?
+				if (objDecl != _objDecl) {
+					std::cerr << "FixInit: non-unique object decl " << objDecl->location << objDecl->name << std::endl;
+				}
+				// a decision should have been made by the resolver, so ctor and init are not both non-NULL
+				assert( ! ctorInit->ctor || ! ctorInit->init );
+				if ( const ast::Stmt * ctor = ctorInit->ctor ) {
+					if ( objDecl->storage.is_static ) {
+						// originally wanted to take advantage of gcc nested functions, but
+						// we get memory errors with this approach. To remedy this, the static
+						// variable is hoisted when the destructor needs to be called.
+						//
+						// generate:
+						// static T __objName_static_varN;
+						// void __objName_dtor_atexitN() {
+						//   __dtor__...;
+						// }
+						// int f(...) {
+						//   ...
+						//   static bool __objName_uninitialized = true;
+						//   if (__objName_uninitialized) {
+						//     __ctor(__objName);
+						//     __objName_uninitialized = false;
+						//     atexit(__objName_dtor_atexitN);
+						//   }
+						//   ...
+						// }
+
+						static UniqueName dtorCallerNamer( "_dtor_atexit" );
+
+						// static bool __objName_uninitialized = true
+						auto boolType = new ast::BasicType( ast::BasicType::Kind::Bool );
+						auto boolInitExpr = new ast::SingleInit(loc, ast::ConstantExpr::from_int(loc, 1 ) );
+						auto isUninitializedVar = new ast::ObjectDecl(loc, objDecl->mangleName + "_uninitialized", boolType, boolInitExpr, ast::Storage::Static, ast::Linkage::Cforall);
+						isUninitializedVar->fixUniqueId();
+
+						// __objName_uninitialized = false;
+						auto setTrue = new ast::UntypedExpr(loc, new ast::NameExpr(loc, "?=?" ) );
+						setTrue->args.push_back( new ast::VariableExpr(loc, isUninitializedVar ) );
+						setTrue->args.push_back( ast::ConstantExpr::from_int(loc, 0 ) );
+
+						// generate body of if
+						auto initStmts = new ast::CompoundStmt(loc);
+						auto & body = initStmts->kids;
+						body.push_back( ctor );
+						body.push_back( new ast::ExprStmt(loc, setTrue ) );
+
+						// put it all together
+						auto ifStmt = new ast::IfStmt(loc, new ast::VariableExpr(loc, isUninitializedVar ), initStmts, 0 );
+						stmtsToAddAfter.push_back( new ast::DeclStmt(loc, isUninitializedVar ) );
+						stmtsToAddAfter.push_back( ifStmt );
+
+						const ast::Stmt * dtor = ctorInit->dtor;
+
+						// these should be automatically managed once reassigned
+						// objDecl->set_init( nullptr );
+						// ctorInit->set_ctor( nullptr );
+						// ctorInit->set_dtor( nullptr );
+						if ( dtor ) {
+							// if the object has a non-trivial destructor, have to
+							// hoist it and the object into the global space and
+							// call the destructor function with atexit.
+
+							// Statement * dtorStmt = dtor->clone();
+
+							// void __objName_dtor_atexitN(...) {...}
+							ast::FunctionDecl * dtorCaller = new ast::FunctionDecl(loc, objDecl->mangleName + dtorCallerNamer.newName(), {}, {}, {}, new ast::CompoundStmt(loc, {dtor}), ast::Storage::Static, ast::Linkage::C );
+							dtorCaller->fixUniqueId();
+							// dtorCaller->stmts->push_back( dtor );
+
+							// atexit(dtor_atexit);
+							auto callAtexit = new ast::UntypedExpr(loc, new ast::NameExpr(loc, "atexit" ) );
+							callAtexit->args.push_back( new ast::VariableExpr(loc, dtorCaller ) );
+
+							body.push_back( new ast::ExprStmt(loc, callAtexit ) );
+
+							// hoist variable and dtor caller decls to list of decls that will be added into global scope
+							staticDtorDecls.push_back( objDecl );
+							staticDtorDecls.push_back( dtorCaller );
+
+							// need to rename object uniquely since it now appears
+							// at global scope and there could be multiple function-scoped
+							// static variables with the same name in different functions.
+							// Note: it isn't sufficient to modify only the mangleName, because
+							// then subsequent Indexer passes can choke on seeing the object's name
+							// if another object has the same name and type. An unfortunate side-effect
+							// of renaming the object is that subsequent NameExprs may fail to resolve,
+							// but there shouldn't be any remaining past this point.
+							static UniqueName staticNamer( "_static_var" );
+							objDecl->name = objDecl->name + staticNamer.newName();
+							objDecl->mangleName = Mangle::mangle( objDecl );
+
+							// xxx - temporary hack: need to return a declaration, but want to hoist the current object out of this scope
+							// create a new object which is never used
+							static UniqueName dummyNamer( "_dummy" );
+							auto dummy = new ast::ObjectDecl(loc, dummyNamer.newName(), new ast::PointerType(new ast::VoidType()), nullptr, ast::Storage::Static, ast::Linkage::Cforall, 0, { new ast::Attribute("unused") } );
+							// delete ctorInit;
+							return dummy;
+						} else {
+							objDecl->init = nullptr;
+							return objDecl;
+						}
+					} else {
+						auto implicit = strict_dynamic_cast< const ast::ImplicitCtorDtorStmt * > ( ctor );
+						auto ctorStmt = implicit->callStmt.as<ast::ExprStmt>();
+						const ast::ApplicationExpr * ctorCall = nullptr;
+						if ( ctorStmt && (ctorCall = isIntrinsicCallExpr( ctorStmt->expr )) && ctorCall->args.size() == 2 ) {
+							// clean up intrinsic copy constructor calls by making them into SingleInits
+							const ast::Expr * ctorArg = ctorCall->args.back();
+							// ctorCall should be gone afterwards
+							auto mutArg = mutate(ctorArg);
+							mutArg->env = ctorCall->env;
+							// std::swap( ctorArg->env, ctorCall->env );
+							objDecl->init = new ast::SingleInit(loc, mutArg );
+
+							// ctorCall->args.pop_back();
+						} else {
+							stmtsToAddAfter.push_back( ctor );
+							objDecl->init = nullptr;
+							// ctorInit->ctor = nullptr;
+						}
+
+						const ast::Stmt * dtor = ctorInit->dtor;
+						if ( dtor ) {
+							auto implicit = strict_dynamic_cast< const ast::ImplicitCtorDtorStmt * >( dtor );
+							const ast::Stmt * dtorStmt = implicit->callStmt;
+
+							// don't need to call intrinsic dtor, because it does nothing, but
+							// non-intrinsic dtors must be called
+							if ( ! isIntrinsicSingleArgCallStmt( dtorStmt ) ) {
+								// set dtor location to the object's location for error messages
+								auto dtorFunc = getDtorFunc( objDecl, dtorStmt, stmtsToAddBefore );
+								objDecl->attributes.push_back( new ast::Attribute( "cleanup", { new ast::VariableExpr(loc, dtorFunc ) } ) );
+								// ctorInit->dtor = nullptr;
+							} // if
+						}
+					} // if
+				} else if ( const ast::Init * init = ctorInit->init ) {
+					objDecl->init = init;
+					// ctorInit->init = nullptr;
+				} else {
+					// no constructor and no initializer, which is okay
+					objDecl->init = nullptr;
+				} // if
+				// delete ctorInit;
+				return objDecl;
+			} // if
+			return _objDecl;
+		}
+
+		void ObjDeclCollector::previsit( const ast::CompoundStmt * ) {
+			GuardValue( curVars );
+		}
+
+		void ObjDeclCollector::previsit( const ast::DeclStmt * stmt ) {
+			// keep track of all variables currently in scope
+			if ( auto objDecl = stmt->decl.as<ast::ObjectDecl>() ) {
+				curVars.push_back( objDecl );
+			} // if
+		}
+
+		void LabelFinder::previsit( const ast::Stmt * stmt ) {
+			// for each label, remember the variables in scope at that label.
+			for ( auto l : stmt->labels ) {
+				vars[l] = curVars;
+			} // for
+		}
+
+		void LabelFinder::previsit( const ast::CompoundStmt * stmt ) {
+			previsit( (const ast::Stmt *) stmt );
+			Parent::previsit( stmt );
+		}
+
+		void LabelFinder::previsit( const ast::DeclStmt * stmt ) {
+			previsit( (const ast::Stmt *)stmt );
+			Parent::previsit( stmt );
+		}
+
+
+		void InsertDtors::previsit( const ast::FunctionDecl * funcDecl ) {
+			// each function needs to have its own set of labels
+			GuardValue( labelVars );
+			labelVars.clear();
+			// LabelFinder does not recurse into FunctionDecl, so need to visit
+			// its children manually.
+			if (funcDecl->type) funcDecl->type->accept(finder);
+			// maybeAccept( funcDecl->type, finder );
+			if (funcDecl->stmts) funcDecl->stmts->accept(finder) ;
+
+			// all labels for this function have been collected, insert destructors as appropriate via implicit recursion.
+		}
+
+		// Handle break/continue/goto in the same manner as C++.  Basic idea: any objects that are in scope at the
+		// BranchStmt but not at the labelled (target) statement must be destructed.  If there are any objects in scope
+		// at the target location but not at the BranchStmt then those objects would be uninitialized so notify the user
+		// of the error.  See C++ Reference 6.6 Jump Statements for details.
+		void InsertDtors::handleGoto( const ast::BranchStmt * stmt ) {
+			// can't do anything for computed goto
+			if ( stmt->computedTarget ) return;
+
+			assertf( stmt->target.name != "", "BranchStmt missing a label: %s", toString( stmt ).c_str() );
+			// S_L = lvars = set of objects in scope at label definition
+			// S_G = curVars = set of objects in scope at goto statement
+			ObjectSet & lvars = labelVars[ stmt->target ];
+
+			DTOR_PRINT(
+				std::cerr << "at goto label: " << stmt->target.name << std::endl;
+				std::cerr << "S_G = " << printSet( curVars ) << std::endl;
+				std::cerr << "S_L = " << printSet( lvars ) << std::endl;
+			)
+
+
+			// std::set_difference requires that the inputs be sorted.
+			lvars.sort();
+			curVars.sort();
+
+			ObjectSet diff;
+			// S_L-S_G results in set of objects whose construction is skipped - it's an error if this set is non-empty
+			std::set_difference( lvars.begin(), lvars.end(), curVars.begin(), curVars.end(), std::inserter( diff, diff.begin() ) );
+			DTOR_PRINT(
+				std::cerr << "S_L-S_G = " << printSet( diff ) << std::endl;
+			)
+			if ( ! diff.empty() ) {
+				SemanticError( stmt, std::string("jump to label '") + stmt->target.name + "' crosses initialization of " + (*diff.begin())->name + " " );
+			} // if
+		}
+
+		void InsertDtors::previsit( const ast::BranchStmt * stmt ) {
+			switch( stmt->kind ) {
+			  case ast::BranchStmt::Continue:
+			  case ast::BranchStmt::Break:
+				// could optimize the break/continue case, because the S_L-S_G check is unnecessary (this set should
+				// always be empty), but it serves as a small sanity check.
+			  case ast::BranchStmt::Goto:
+				handleGoto( stmt );
+				break;
+			  default:
+				assert( false );
+			} // switch
+		}
+
+		bool checkWarnings( const ast::FunctionDecl * funcDecl ) {
+			// only check for warnings if the current function is a user-defined
+			// constructor or destructor
+			if ( ! funcDecl ) return false;
+			if ( ! funcDecl->stmts ) return false;
+			return CodeGen::isCtorDtor( funcDecl->name ) && ! funcDecl->linkage.is_overrideable;
+		}
+
+		void GenStructMemberCalls::previsit( const ast::FunctionDecl * funcDecl ) {
+			GuardValue( function );
+			GuardValue( unhandled );
+			GuardValue( usedUninit );
+			GuardValue( thisParam );
+			GuardValue( isCtor );
+			GuardValue( structDecl );
+			errors = SemanticErrorException();  // clear previous errors
+
+			// need to start with fresh sets
+			unhandled.clear();
+			usedUninit.clear();
+
+			function = mutate(funcDecl);
+			// could this be non-unique?
+			if (function != funcDecl) {
+				std::cerr << "GenStructMemberCalls: non-unique FunctionDecl " << funcDecl->location << funcDecl->name << std::endl;
+			}
+
+			isCtor = CodeGen::isConstructor( function->name );
+			if ( checkWarnings( function ) ) {
+				// const ast::FunctionType * type = function->type;
+				// assert( ! type->params.empty() );
+				thisParam = function->params.front().strict_as<ast::ObjectDecl>();
+				auto thisType = getPointerBase( thisParam->get_type() );
+				auto structType = dynamic_cast< const ast::StructInstType * >( thisType );
+				if ( structType ) {
+					structDecl = structType->base;
+					for ( auto & member : structDecl->members ) {
+						if ( auto field = member.as<ast::ObjectDecl>() ) {
+							// record all of the struct type's members that need to be constructed or
+							// destructed by the end of the function
+							unhandled.insert( field );
+						}
+					}
+				}
+			}
+		}
+
+		const ast::DeclWithType * GenStructMemberCalls::postvisit( const ast::FunctionDecl * funcDecl ) {
+			// remove the unhandled objects from usedUninit, because a call is inserted
+			// to handle them - only objects that are later constructed are used uninitialized.
+			std::map< const ast::DeclWithType *, CodeLocation > diff;
+			// need the comparator since usedUninit and unhandled have different types
+			struct comp_t {
+				typedef decltype(usedUninit)::value_type usedUninit_t;
+				typedef decltype(unhandled)::value_type unhandled_t;
+				bool operator()(usedUninit_t x, unhandled_t y) { return x.first < y; }
+				bool operator()(unhandled_t x, usedUninit_t y) { return x < y.first; }
+			} comp;
+			std::set_difference( usedUninit.begin(), usedUninit.end(), unhandled.begin(), unhandled.end(), std::inserter( diff, diff.begin() ), comp );
+			for ( auto p : diff ) {
+				auto member = p.first;
+				auto loc = p.second;
+				// xxx - make error message better by also tracking the location that the object is constructed at?
+				emit( loc, "in ", function->name, ", field ", member->name, " used before being constructed" );
+			}
+
+			const CodeLocation loc = funcDecl->location;
+
+			if ( ! unhandled.empty() ) {
+				auto mutStmts = function->stmts.get_and_mutate();
+				// need to explicitly re-add function parameters to the indexer in order to resolve copy constructors
+				auto guard = makeFuncGuard( [this]() { symtab.enterScope(); }, [this]() { symtab.leaveScope(); } );
+				symtab.addFunction( function );
+
+				// need to iterate through members in reverse in order for
+				// ctor/dtor statements to come out in the right order
+				for ( auto & member : reverseIterate( structDecl->members ) ) {
+					auto field = member.as<ast::ObjectDecl>();
+					// skip non-DWT members
+					if ( ! field ) continue;
+					// skip non-constructable members
+					if ( ! tryConstruct( field ) ) continue;
+					// skip handled members
+					if ( ! unhandled.count( field ) ) continue;
+
+					// insert and resolve default/copy constructor call for each field that's unhandled
+					// std::list< const ast::Stmt * > stmt;
+					ast::Expr * arg2 = nullptr;
+					if ( function->name == "?{}" && isCopyFunction( function ) ) {
+						// if copy ctor, need to pass second-param-of-this-function.field
+						// std::list< DeclarationWithType * > & params = function->get_functionType()->get_parameters();
+						assert( function->params.size() == 2 );
+						arg2 = new ast::MemberExpr(funcDecl->location, field, new ast::VariableExpr(funcDecl->location, function->params.back() ) );
+					}
+					InitExpander_new srcParam( arg2 );
+					// cast away reference type and construct field.
+					ast::Expr * thisExpr = new ast::CastExpr(funcDecl->location, new ast::VariableExpr(funcDecl->location, thisParam ), thisParam->get_type()->stripReferences());
+					ast::Expr * memberDest = new ast::MemberExpr(funcDecl->location, field, thisExpr );
+					ast::ptr<ast::Stmt> callStmt = SymTab::genImplicitCall( srcParam, memberDest, loc, function->name, field, static_cast<SymTab::LoopDirection>(isCtor) );
+
+					if ( callStmt ) {
+						// auto & callStmt = stmt.front();
+
+						try {
+							callStmt = callStmt->accept( *visitor );
+							if ( isCtor ) {
+								mutStmts->push_front( callStmt );
+							} else { // TODO: don't generate destructor function/object for intrinsic calls
+								// destructor statements should be added at the end
+								// function->get_statements()->push_back( callStmt );
+
+								// Optimization: do not need to call intrinsic destructors on members
+								if ( isIntrinsicSingleArgCallStmt( callStmt ) ) continue;
+
+								// __Destructor _dtor0 = { (void *)&b.a1, (void (*)(void *)_destroy_A };
+								std::list< ast::ptr<ast::Stmt> > stmtsToAdd;
+
+								static UniqueName memberDtorNamer = { "__memberDtor" };
+								assertf( Validate::dtorStruct, "builtin __Destructor not found." );
+								assertf( Validate::dtorStructDestroy, "builtin __destroy_Destructor not found." );
+
+								ast::Expr * thisExpr = new ast::CastExpr( new ast::AddressExpr( new ast::VariableExpr(loc, thisParam ) ), new ast::PointerType( new ast::VoidType(), ast::CV::Qualifiers() ) );
+								ast::Expr * dtorExpr = new ast::VariableExpr(loc, getDtorFunc( thisParam, callStmt, stmtsToAdd ) );
+
+								// cast destructor pointer to void (*)(void *), to silence GCC incompatible pointer warnings
+								auto dtorFtype = new ast::FunctionType();
+								dtorFtype->params.emplace_back( new ast::PointerType( new ast::VoidType() ) );
+								auto dtorType = new ast::PointerType( dtorFtype );
+
+								auto destructor = new ast::ObjectDecl(loc, memberDtorNamer.newName(), new ast::StructInstType( ast::dtorStruct ), new ast::ListInit(loc, { new ast::SingleInit(loc, thisExpr ), new ast::SingleInit(loc, new ast::CastExpr( dtorExpr, dtorType ) ) } ) );
+								destructor->attributes.push_back( new ast::Attribute( "cleanup", { new ast::VariableExpr({}, ast::dtorStructDestroy ) } ) );
+								mutStmts->push_front( new ast::DeclStmt(loc, destructor ) );
+								mutStmts->kids.splice( mutStmts->kids.begin(), stmtsToAdd );
+							}
+						} catch ( SemanticErrorException & error ) {
+							emit( funcDecl->location, "in ", function->name , ", field ", field->name, " not explicitly ", isCtor ? "constructed" : "destructed",  " and no ", isCtor ? "default constructor" : "destructor", " found" );
+						}
+					}
+				}
+				function->stmts = mutStmts;
+			}
+			if (! errors.isEmpty()) {
+				throw errors;
+			}
+			// return funcDecl;
+			return function;
+		}
+
+		/// true if expr is effectively just the 'this' parameter
+		bool isThisExpression( const ast::Expr * expr, const ast::DeclWithType * thisParam ) {
+			// TODO: there are more complicated ways to pass 'this' to a constructor, e.g. &*, *&, etc.
+			if ( auto varExpr = dynamic_cast< const ast::VariableExpr * >( expr ) ) {
+				return varExpr->var == thisParam;
+			} else if ( auto castExpr = dynamic_cast< const ast::CastExpr * > ( expr ) ) {
+				return isThisExpression( castExpr->arg, thisParam );
+			}
+			return false;
+		}
+
+		/// returns a MemberExpr if expr is effectively just member access on the 'this' parameter, else nullptr
+		const ast::MemberExpr * isThisMemberExpr( const ast::Expr * expr, const ast::DeclWithType * thisParam ) {
+			if ( auto memberExpr = dynamic_cast< const ast::MemberExpr * >( expr ) ) {
+				if ( isThisExpression( memberExpr->aggregate, thisParam ) ) {
+					return memberExpr;
+				}
+			} else if ( auto castExpr = dynamic_cast< const ast::CastExpr * >( expr ) ) {
+				return isThisMemberExpr( castExpr->arg, thisParam );
+			}
+			return nullptr;
+		}
+
+		void GenStructMemberCalls::previsit( const ast::ApplicationExpr * appExpr ) {
+			if ( ! checkWarnings( function ) ) {
+				visit_children = false;
+				return;
+			}
+
+			std::string fname = getFunctionName( appExpr );
+			if ( fname == function->name ) {
+				// call to same kind of function
+				const ast::Expr * firstParam = appExpr->args.front();
+
+				if ( isThisExpression( firstParam, thisParam ) ) {
+					// if calling another constructor on thisParam, assume that function handles
+					// all members - if it doesn't a warning will appear in that function.
+					unhandled.clear();
+				} else if ( auto memberExpr = isThisMemberExpr( firstParam, thisParam ) ) {
+					// if first parameter is a member expression on the this parameter,
+					// then remove the member from unhandled set.
+					if ( isThisExpression( memberExpr->aggregate, thisParam ) ) {
+						unhandled.erase( memberExpr->member );
+					}
+				}
+			}
+		}
+
+		void GenStructMemberCalls::previsit( const ast::MemberExpr * memberExpr ) {
+			if ( ! checkWarnings( function ) || ! isCtor ) {
+				visit_children = false;
+				return;
+			}
+
+			if ( isThisExpression( memberExpr->aggregate, thisParam ) ) {
+				if ( unhandled.count( memberExpr->member ) ) {
+					// emit a warning because a member was used before it was constructed
+					usedUninit.insert( { memberExpr->member, memberExpr->location } );
+				}
+			}
+		}
+
+		template< typename Visitor, typename... Params >
+		void error( Visitor & v, CodeLocation loc, const Params &... params ) {
+			SemanticErrorException err( loc, toString( params... ) );
+			v.errors.append( err );
+		}
+
+		template< typename... Params >
+		void GenStructMemberCalls::emit( CodeLocation loc, const Params &... params ) {
+			// toggle warnings vs. errors here.
+			// warn( params... );
+			error( *this, loc, params... );
+		}
+
+		const ast::Expr * GenStructMemberCalls::postvisit( const ast::UntypedExpr * untypedExpr ) {
+			// Expression * newExpr = untypedExpr;
+			// xxx - functions returning ast::ptr seems wrong...
+			auto res = ResolvExpr::findVoidExpression( untypedExpr, symtab );
+			return res.release();
+			// return newExpr;
+		}
+
+		void InsertImplicitCalls::previsit(const ast::UniqueExpr * unqExpr) {
+			if (visitedIds.count(unqExpr->id)) visit_children = false;
+			else visitedIds.insert(unqExpr->id);
+		}
+
+		const ast::Expr * FixCtorExprs::postvisit( const ast::ConstructorExpr * ctorExpr ) {
+			const CodeLocation loc = ctorExpr->location;
+			static UniqueName tempNamer( "_tmp_ctor_expr" );
+			// xxx - is the size check necessary?
+			assert( ctorExpr->result && ctorExpr->result->size() == 1 );
+
+			// xxx - this can be TupleAssignExpr now. Need to properly handle this case.
+			// take possession of expr and env
+			ast::ptr<ast::ApplicationExpr> callExpr = ctorExpr->callExpr.strict_as<ast::ApplicationExpr>();
+			ast::ptr<ast::TypeSubstitution> env = ctorExpr->env;
+			// ctorExpr->set_callExpr( nullptr );
+			// ctorExpr->set_env( nullptr );
+
+			// xxx - ideally we would reuse the temporary generated from the copy constructor passes from within firstArg if it exists and not generate a temporary if it's unnecessary.
+			auto tmp = new ast::ObjectDecl(loc, tempNamer.newName(), callExpr->args.front()->result );
+			declsToAddBefore.push_back( tmp );
+			// delete ctorExpr;
+
+			// build assignment and replace constructor's first argument with new temporary
+			auto mutCallExpr = callExpr.get_and_mutate();
+			const ast::Expr * firstArg = callExpr->args.front();
+			ast::Expr * assign = new ast::UntypedExpr(loc, new ast::NameExpr(loc, "?=?" ), { new ast::AddressExpr(loc, new ast::VariableExpr(loc, tmp ) ), new ast::AddressExpr( firstArg ) } );
+			firstArg = new ast::VariableExpr(loc, tmp );
+			mutCallExpr->args.front() = firstArg;
+
+			// resolve assignment and dispose of new env
+			auto resolved = ResolvExpr::findVoidExpression( assign, symtab );
+			auto mut = resolved.get_and_mutate();
+			assertf(resolved.get() == mut, "newly resolved expression must be unique");
+			mut->env = nullptr;
+
+			// for constructor expr:
+			//   T x;
+			//   x{};
+			// results in:
+			//   T x;
+			//   T & tmp;
+			//   &tmp = &x, ?{}(tmp), tmp
+			ast::CommaExpr * commaExpr = new ast::CommaExpr(loc, resolved, new ast::CommaExpr(loc, mutCallExpr, new ast::VariableExpr(loc, tmp ) ) );
+			commaExpr->env = env;
+			return commaExpr;
+		}
+	} // namespace
+} // namespace InitTweak
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/InitTweak/GenInit.cc
===================================================================
--- src/InitTweak/GenInit.cc	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ src/InitTweak/GenInit.cc	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -283,4 +283,11 @@
 		assert( stmts.size() <= 1 );
 		return stmts.size() == 1 ? strict_dynamic_cast< ImplicitCtorDtorStmt * >( stmts.front() ) : nullptr;
+
+	}
+
+	ast::ptr<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_new srcParam(arg);
+		return SymTab::genImplicitCall(srcParam, new ast::VariableExpr(loc, objDecl), loc, fname, objDecl);
 	}
 
Index: src/InitTweak/GenInit.h
===================================================================
--- src/InitTweak/GenInit.h	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ src/InitTweak/GenInit.h	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -33,4 +33,5 @@
 	/// generates a single ctor/dtor statement using objDecl as the 'this' parameter and arg as the optional argument
 	ImplicitCtorDtorStmt * genCtorDtor( const std::string & fname, ObjectDecl * objDecl, Expression * arg = nullptr );
+	ast::ptr<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
Index: src/InitTweak/InitTweak.cc
===================================================================
--- src/InitTweak/InitTweak.cc	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ src/InitTweak/InitTweak.cc	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -498,4 +498,11 @@
 	}
 
+	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", toString( func ).c_str());
+		return params.front().strict_as<ast::ObjectDecl>();
+	}
+
 	bool tryConstruct( DeclarationWithType * dwt ) {
 		ObjectDecl * objDecl = dynamic_cast< ObjectDecl * >( dwt );
@@ -511,4 +518,18 @@
 	}
 
+	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 );
+	}
+
 	struct CallFinder_old {
 		CallFinder_old( const std::list< std::string > & names ) : names( names ) {}
@@ -536,5 +557,5 @@
 
 	struct CallFinder_new final {
-		std::vector< ast::ptr< ast::Expr > > matches;
+		std::vector< const ast::Expr * > matches;
 		const std::vector< std::string > names;
 
@@ -558,5 +579,5 @@
 	}
 
-	std::vector< ast::ptr< ast::Expr > > collectCtorDtorCalls( const ast::Stmt * stmt ) {
+	std::vector< const ast::Expr * > collectCtorDtorCalls( const ast::Stmt * stmt ) {
 		ast::Pass< CallFinder_new > finder{ std::vector< std::string >{ "?{}", "^?{}" } };
 		maybe_accept( stmt, finder );
@@ -696,5 +717,5 @@
 		template <typename Predicate>
 		bool allofCtorDtor( const ast::Stmt * stmt, const Predicate & pred ) {
-			std::vector< ast::ptr< ast::Expr > > callExprs = collectCtorDtorCalls( stmt );
+			std::vector< const ast::Expr * > callExprs = collectCtorDtorCalls( stmt );
 			return std::all_of( callExprs.begin(), callExprs.end(), pred );
 		}
@@ -939,4 +960,31 @@
 	}
 
+	// 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({}, "T", {}, nullptr, ast::TypeDecl::Dtype, true);
+			assign = new ast::FunctionDecl({}, "?=?", {}, 
+			{ new ast::ObjectDecl({}, "_dst", new ast::ReferenceType(new ast::TypeInstType("T", td))),
+			  new ast::ObjectDecl({}, "_src", new ast::TypeInstType("T", td))},
+			{ new ast::ObjectDecl({}, "_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);
+			}
+		}
+		return new ast::ApplicationExpr(dst->location, ast::VariableExpr::functionPointer(dst->location, assign), {dst, src});
+	}
+
 	struct ConstExprChecker : public WithShortCircuiting {
 		// most expressions are not const expr
@@ -1055,3 +1103,13 @@
 		return isCopyFunction( decl, "?{}" );
 	}
+
+	void addDataSectonAttribute( ObjectDecl * objDecl ) {
+		Type *strLitT = new PointerType( Type::Qualifiers( ),
+			new BasicType( Type::Qualifiers( ), BasicType::Char ) );
+		std::list< Expression * > attr_params;
+		attr_params.push_back( 
+			new ConstantExpr( Constant( strLitT, "\".data#\"", std::nullopt ) ) );
+		objDecl->attributes.push_back(new Attribute("section", attr_params));
+	}
+
 }
Index: src/InitTweak/InitTweak.h
===================================================================
--- src/InitTweak/InitTweak.h	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ src/InitTweak/InitTweak.h	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -38,7 +38,10 @@
 	/// returns the first parameter of a constructor/destructor/assignment function
 	ObjectDecl * getParamThis( FunctionType * ftype );
+	const ast::ObjectDecl * getParamThis(const ast::FunctionDecl * func);
 
 	/// generate a bitwise assignment operation.
 	ApplicationExpr * createBitwiseAssignment( Expression * dst, Expression * src );
+
+	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
@@ -48,7 +51,9 @@
 	/// True if the resolver should try to construct dwt
 	bool tryConstruct( DeclarationWithType * dwt );
+	bool tryConstruct( const ast::DeclWithType * dwt );
 
 	/// True if the type can have a user-defined constructor
 	bool isConstructable( Type * t );
+	bool isConstructable( const ast::Type * t );
 
 	/// True if the Initializer contains designations
@@ -79,5 +84,5 @@
 	/// get all Ctor/Dtor call expressions from a Statement
 	void collectCtorDtorCalls( Statement * stmt, std::list< Expression * > & matches );
-	std::vector< ast::ptr< ast::Expr > > collectCtorDtorCalls( const ast::Stmt * stmt );
+	std::vector< const ast::Expr * > collectCtorDtorCalls( const ast::Stmt * stmt );
 
 	/// get the Ctor/Dtor call expression from a Statement that looks like a generated ctor/dtor call
@@ -102,4 +107,15 @@
 	bool isConstExpr( Expression * expr );
 	bool isConstExpr( Initializer * 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 addDataSectonAttribute( ObjectDecl * objDecl );
 
 	class InitExpander_old {
Index: src/InitTweak/module.mk
===================================================================
--- src/InitTweak/module.mk	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ src/InitTweak/module.mk	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -23,5 +23,6 @@
 	InitTweak/GenInit.h \
 	InitTweak/InitTweak.cc \
-	InitTweak/InitTweak.h
+	InitTweak/InitTweak.h \
+	InitTweak/FixInitNew.cpp
 
 SRCDEMANGLE += \
Index: src/Parser/ParseNode.h
===================================================================
--- src/Parser/ParseNode.h	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ src/Parser/ParseNode.h	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -10,6 +10,6 @@
 // Created On       : Sat May 16 13:28:16 2015
 // Last Modified By : Peter A. Buhr
-// Last Modified On : Mon Jul  6 09:33:32 2020
-// Update Count     : 892
+// Last Modified On : Sat Oct 24 03:53:54 2020
+// Update Count     : 895
 //
 
@@ -205,6 +205,5 @@
 struct TypeData;
 
-class DeclarationNode : public ParseNode {
-  public:
+struct DeclarationNode : public ParseNode {
 	// These enumerations must harmonize with their names in DeclarationNode.cc.
 	enum BasicType { Void, Bool, Char, Int, Int128,
@@ -304,5 +303,5 @@
 	bool get_inLine() const { return inLine; }
 	DeclarationNode * set_inLine( bool inL ) { inLine = inL; return this; }
-  public:
+
 	DeclarationNode * get_last() { return (DeclarationNode *)ParseNode::get_last(); }
 
@@ -360,6 +359,5 @@
 //##############################################################################
 
-class StatementNode final : public ParseNode {
-  public:
+struct StatementNode final : public ParseNode {
 	StatementNode() { stmt = nullptr; }
 	StatementNode( Statement * stmt ) : stmt( stmt ) {}
@@ -382,5 +380,5 @@
 		os << stmt.get() << std::endl;
 	}
-  private:
+
 	std::unique_ptr<Statement> stmt;
 }; // StatementNode
@@ -426,4 +424,5 @@
 Statement * build_finally( StatementNode * stmt );
 Statement * build_compound( StatementNode * first );
+StatementNode * maybe_build_compound( StatementNode * first );
 Statement * build_asm( bool voltile, Expression * instruction, ExpressionNode * output = nullptr, ExpressionNode * input = nullptr, ExpressionNode * clobber = nullptr, LabelNode * gotolabels = nullptr );
 Statement * build_directive( std::string * directive );
Index: src/Parser/StatementNode.cc
===================================================================
--- src/Parser/StatementNode.cc	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ src/Parser/StatementNode.cc	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -10,6 +10,6 @@
 // Created On       : Sat May 16 14:59:41 2015
 // Last Modified By : Peter A. Buhr
-// Last Modified On : Sat Aug  4 09:39:25 2018
-// Update Count     : 363
+// Last Modified On : Sat Oct 24 04:20:55 2020
+// Update Count     : 383
 //
 
@@ -345,4 +345,19 @@
 } // 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( 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<CompoundStmt *>( first->stmt.get() ) ) { // unique_ptr
+		CompoundStmt * cs = new CompoundStmt();
+		buildMoveList( first, cs->get_kids() );
+		return new StatementNode( cs );
+	} // if
+	return first;
+} // maybe_build_compound
+
 Statement * build_asm( bool voltile, Expression * instruction, ExpressionNode * output, ExpressionNode * input, ExpressionNode * clobber, LabelNode * gotolabels ) {
 	std::list< Expression * > out, in;
Index: src/Parser/parser.yy
===================================================================
--- src/Parser/parser.yy	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ src/Parser/parser.yy	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -10,6 +10,6 @@
 // Created On       : Sat Sep  1 20:22:55 2001
 // Last Modified By : Peter A. Buhr
-// Last Modified On : Fri Oct  9 18:09:09 2020
-// Update Count     : 4614
+// Last Modified On : Sat Oct 24 08:21:14 2020
+// Update Count     : 4624
 //
 
@@ -1080,7 +1080,7 @@
 	IF '(' if_control_expression ')' statement			%prec THEN
 		// explicitly deal with the shift/reduce conflict on if/else
-		{ $$ = new StatementNode( build_if( $3, $5, nullptr ) ); }
+		{ $$ = new StatementNode( build_if( $3, maybe_build_compound( $5 ), nullptr ) ); }
 	| IF '(' if_control_expression ')' statement ELSE statement
-		{ $$ = new StatementNode( build_if( $3, $5, $7 ) ); }
+		{ $$ = new StatementNode( build_if( $3, maybe_build_compound( $5 ), maybe_build_compound( $7 ) ) ); }
 	;
 
@@ -1130,5 +1130,5 @@
 
 case_clause:											// CFA
-	case_label_list statement					{ $$ = $1->append_last_case( new StatementNode( build_compound( $2 ) ) ); }
+	case_label_list statement					{ $$ = $1->append_last_case( maybe_build_compound( $2 ) ); }
 	;
 
@@ -1148,15 +1148,15 @@
 iteration_statement:
 	WHILE '(' push if_control_expression ')' statement pop
-		{ $$ = new StatementNode( build_while( $4, $6 ) ); }
+		{ $$ = new StatementNode( build_while( $4, maybe_build_compound( $6 ) ) ); }
 	| WHILE '(' ')' statement							// CFA => while ( 1 )
-		{ $$ = new StatementNode( build_while( new IfCtrl( nullptr, new ExpressionNode( build_constantInteger( *new string( "1" ) ) ) ), $4 ) ); }
+		{ $$ = new StatementNode( build_while( new IfCtrl( nullptr, new ExpressionNode( build_constantInteger( *new string( "1" ) ) ) ), maybe_build_compound( $4 ) ) ); }
 	| DO statement WHILE '(' comma_expression ')' ';'
-		{ $$ = new StatementNode( build_do_while( $5, $2 ) ); }
+		{ $$ = new StatementNode( build_do_while( $5, maybe_build_compound( $2 ) ) ); }
 	| DO statement WHILE '(' ')' ';'					// CFA => do while( 1 )
-		{ $$ = new StatementNode( build_do_while( new ExpressionNode( build_constantInteger( *new string( "1" ) ) ), $2 ) ); }
+		{ $$ = new StatementNode( build_do_while( new ExpressionNode( build_constantInteger( *new string( "1" ) ) ), maybe_build_compound( $2 ) ) ); }
 	| FOR '(' push for_control_expression_list ')' statement pop
-		{ $$ = new StatementNode( build_for( $4, $6 ) ); }
+		{ $$ = new StatementNode( build_for( $4, maybe_build_compound( $6 ) ) ); }
 	| FOR '(' ')' statement								// CFA => for ( ;; )
-		{ $$ = new StatementNode( build_for( new ForCtrl( (ExpressionNode * )nullptr, (ExpressionNode * )nullptr, (ExpressionNode * )nullptr ), $4 ) ); }
+		{ $$ = new StatementNode( build_for( new ForCtrl( (ExpressionNode * )nullptr, (ExpressionNode * )nullptr, (ExpressionNode * )nullptr ), maybe_build_compound( $4 ) ) ); }
 	;
 
@@ -1341,16 +1341,16 @@
 waitfor_clause:
 	when_clause_opt waitfor statement					%prec THEN
- 		{ $$ = build_waitfor( $2, $3, $1 ); }
+		{ $$ = build_waitfor( $2, maybe_build_compound( $3 ), $1 ); }
 	| when_clause_opt waitfor statement WOR waitfor_clause
- 		{ $$ = build_waitfor( $2, $3, $1, $5 ); }
+		{ $$ = build_waitfor( $2, maybe_build_compound( $3 ), $1, $5 ); }
 	| when_clause_opt timeout statement					%prec THEN
- 		{ $$ = build_waitfor_timeout( $2, $3, $1 ); }
+		{ $$ = build_waitfor_timeout( $2, maybe_build_compound( $3 ), $1 ); }
 	| when_clause_opt ELSE statement
- 		{ $$ = build_waitfor_timeout( nullptr, $3, $1 ); }
+		{ $$ = build_waitfor_timeout( nullptr, maybe_build_compound( $3 ), $1 ); }
 		// "else" must be conditional after timeout or timeout is never triggered (i.e., it is meaningless)
 	| when_clause_opt timeout statement WOR ELSE statement
 		{ SemanticError( yylloc, "else clause must be conditional after timeout or timeout never triggered." ); $$ = nullptr; }
 	| when_clause_opt timeout statement WOR when_clause ELSE statement
- 		{ $$ = build_waitfor_timeout( $2, $3, $1, $7, $5 ); }
+		{ $$ = build_waitfor_timeout( $2, maybe_build_compound( $3 ), $1, maybe_build_compound( $7 ), $5 ); }
 	;
 
Index: src/ResolvExpr/Resolver.cc
===================================================================
--- src/ResolvExpr/Resolver.cc	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ src/ResolvExpr/Resolver.cc	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -1105,5 +1105,7 @@
 		}
 
-		/// Establish post-resolver invariants for expressions
+		
+	} // anonymous namespace
+/// Establish post-resolver invariants for expressions
 		void finishExpr(
 			ast::ptr< ast::Expr > & expr, const ast::TypeEnvironment & env,
@@ -1118,6 +1120,4 @@
 			StripCasts_new::strip( expr );
 		}
-	} // anonymous namespace
-
 
 	ast::ptr< ast::Expr > resolveInVoidContext(
@@ -1139,6 +1139,5 @@
 	}
 
-	namespace {
-		/// Resolve `untyped` to the expression whose candidate is the best match for a `void`
+	/// Resolve `untyped` to the expression whose candidate is the best match for a `void`
 		/// context.
 		ast::ptr< ast::Expr > findVoidExpression(
@@ -1151,4 +1150,7 @@
 			return newExpr;
 		}
+
+	namespace {
+		
 
 		/// resolve `untyped` to the expression whose candidate satisfies `pred` with the
@@ -1162,5 +1164,5 @@
 			CandidateRef choice =
 				findUnfinishedKindExpression( untyped, symtab, kind, pred, mode );
-			finishExpr( choice->expr, choice->env, untyped->env );
+			ResolvExpr::finishExpr( choice->expr, choice->env, untyped->env );
 			return std::move( choice->expr );
 		}
Index: src/ResolvExpr/Resolver.h
===================================================================
--- src/ResolvExpr/Resolver.h	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ src/ResolvExpr/Resolver.h	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -66,4 +66,6 @@
 	ast::ptr< ast::Expr > findSingleExpression(
 		const ast::Expr * untyped, const ast::Type * type, const ast::SymbolTable & symtab );
+	ast::ptr< ast::Expr > findVoidExpression(
+		const ast::Expr * untyped, const ast::SymbolTable & symtab);
 	/// Resolves a constructor init expression
 	ast::ptr< ast::Init > resolveCtorInit( 
Index: src/SymTab/Autogen.cc
===================================================================
--- src/SymTab/Autogen.cc	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ src/SymTab/Autogen.cc	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -233,4 +233,15 @@
 	}
 
+	// shallow copy the pointer list for return
+	std::vector<ast::ptr<ast::TypeDecl>> getGenericParams (const ast::Type * t) {
+		if (auto structInst = dynamic_cast<const ast::StructInstType*>(t)) {
+			return structInst->base->params;
+		}
+		if (auto unionInst = dynamic_cast<const ast::UnionInstType*>(t)) {
+			return unionInst->base->params;
+		}
+		return {};
+	}
+
 	/// given type T, generate type of default ctor/dtor, i.e. function type void (*) (T *)
 	FunctionType * genDefaultType( Type * paramType, bool maybePolymorphic ) {
@@ -244,4 +255,12 @@
 		ftype->parameters.push_back( dstParam );
 		return ftype;
+	}
+
+	/// 
+	ast::FunctionDecl * genDefaultFunc(const CodeLocation loc, const std::string fname, const ast::Type * paramType, bool maybePolymorphic) {
+		std::vector<ast::ptr<ast::TypeDecl>> typeParams;
+		if (maybePolymorphic) typeParams = getGenericParams(paramType);
+		auto dstParam = new ast::ObjectDecl(loc, "_dst", new ast::ReferenceType(paramType), nullptr, {}, ast::Linkage::Cforall);
+		return new ast::FunctionDecl(loc, fname, std::move(typeParams), {dstParam}, {}, new ast::CompoundStmt(loc));
 	}
 
Index: src/SymTab/Autogen.h
===================================================================
--- src/SymTab/Autogen.h	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ src/SymTab/Autogen.h	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -55,4 +55,6 @@
 	/// maybePolymorphic is true if the resulting FunctionType is allowed to be polymorphic
 	FunctionType * genDefaultType( Type * paramType, bool maybePolymorphic = true );
+
+	ast::FunctionDecl * genDefaultFunc(const CodeLocation loc, const std::string fname, const ast::Type * paramType, bool maybePolymorphic = true);
 
 	/// generate the type of a copy constructor for paramType.
Index: src/SynTree/Expression.h
===================================================================
--- src/SynTree/Expression.h	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ src/SynTree/Expression.h	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -163,4 +163,29 @@
 };
 
+/// VariableExpr represents an expression that simply refers to the value of a named variable.
+/// Does not take ownership of var.
+class VariableExpr : public Expression {
+  public:
+	DeclarationWithType * var;
+
+	VariableExpr();
+	VariableExpr( DeclarationWithType * var );
+	VariableExpr( const VariableExpr & other );
+	virtual ~VariableExpr();
+
+	bool get_lvalue() const final;
+
+	DeclarationWithType * get_var() const { return var; }
+	void set_var( DeclarationWithType * newValue ) { var = newValue; }
+
+	static VariableExpr * functionPointer( FunctionDecl * decl );
+
+	virtual VariableExpr * clone() const override { return new VariableExpr( * this ); }
+	virtual void accept( Visitor & v ) override { v.visit( this ); }
+	virtual void accept( Visitor & v ) const override { v.visit( this ); }
+	virtual Expression * acceptMutator( Mutator & m ) override { return m.mutate( this ); }
+	virtual void print( std::ostream & os, Indenter indent = {} ) const override;
+};
+
 // The following classes are used to represent expression types that cannot be converted into
 // function-call format.
@@ -329,29 +354,4 @@
 };
 
-/// VariableExpr represents an expression that simply refers to the value of a named variable.
-/// Does not take ownership of var.
-class VariableExpr : public Expression {
-  public:
-	DeclarationWithType * var;
-
-	VariableExpr();
-	VariableExpr( DeclarationWithType * var );
-	VariableExpr( const VariableExpr & other );
-	virtual ~VariableExpr();
-
-	bool get_lvalue() const final;
-
-	DeclarationWithType * get_var() const { return var; }
-	void set_var( DeclarationWithType * newValue ) { var = newValue; }
-
-	static VariableExpr * functionPointer( FunctionDecl * decl );
-
-	virtual VariableExpr * clone() const override { return new VariableExpr( * this ); }
-	virtual void accept( Visitor & v ) override { v.visit( this ); }
-	virtual void accept( Visitor & v ) const override { v.visit( this ); }
-	virtual Expression * acceptMutator( Mutator & m ) override { return m.mutate( this ); }
-	virtual void print( std::ostream & os, Indenter indent = {} ) const override;
-};
-
 /// ConstantExpr represents an expression that simply refers to the value of a constant
 class ConstantExpr : public Expression {
Index: src/main.cc
===================================================================
--- src/main.cc	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ src/main.cc	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -343,16 +343,23 @@
 			auto transUnit = convert( move( translationUnit ) );
 			PASS( "Resolve", ResolvExpr::resolve( transUnit ) );
+			if ( exprp ) {
+				translationUnit = convert( move( transUnit ) );
+				dump( translationUnit );
+				return EXIT_SUCCESS;
+			} // if
+
+			PASS( "Fix Init", InitTweak::fix(transUnit, buildingLibrary()));
 			translationUnit = convert( move( transUnit ) );
 		} else {
 			PASS( "Resolve", ResolvExpr::resolve( translationUnit ) );
+			if ( exprp ) {
+				dump( translationUnit );
+				return EXIT_SUCCESS;
+			}
+
+			PASS( "Fix Init", InitTweak::fix( translationUnit, buildingLibrary() ) );
 		}
 
-		if ( exprp ) {
-			dump( translationUnit );
-			return EXIT_SUCCESS;
-		} // if
-
 		// fix ObjectDecl - replaces ConstructorInit nodes
-		PASS( "Fix Init", InitTweak::fix( translationUnit, buildingLibrary() ) );
 		if ( ctorinitp ) {
 			dump ( translationUnit );
Index: tests/.expect/const-init.txt
===================================================================
--- tests/.expect/const-init.txt	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ tests/.expect/const-init.txt	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -1,1 +1,2 @@
-done
+almost done
+dtor
Index: tests/complex.cfa
===================================================================
--- tests/complex.cfa	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ tests/complex.cfa	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -14,5 +14,4 @@
 //
 
-#include <stdio.h>
 #include <complex.h>
 #ifdef __CFA__
Index: tests/const-init.cfa
===================================================================
--- tests/const-init.cfa	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ tests/const-init.cfa	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -16,11 +16,11 @@
 /*
 
-This test shows non-crashing of generated code for constants with interesting initizers.
+These tests show non-crashing of generated code for constants with interesting initializers.
 The potential for these to crash is compiler dependent.
 
 There are two cases:
-1. static constants in one compilation unit (tested here)
+1. static constants in one compilation unit (tested here, in a few sub-cases)
 2. extern constants across compilation units (tested by libcfa being loadable, specifically
-   the constant declarations in libcfa/src/limits.cfa, which almost every test exercises,
+   the constant definitions in libcfa/src/limits.cfa, which almost every test exercises,
    including "hello;" but notably, the "limits" test does not exercise it because that test
    is compile-only)
@@ -37,12 +37,24 @@
 GCC-10 on Ubuntu 20.04    Has crashed      Has crashed
 
-For this test case to fail, with most other tests passing, would be a situation only ever
+For this test to fail, with most other tests passing, would be a situation only ever
 observed with GCC-8.
 
 */
 
+// initailized by generated function, called before main
 static const char foo = -1;
 
+struct thing{};
+void ^?{}( thing & ) { printf("dtor\n"); }
+
 int main() {
-    printf("done\n");
+    // foo is already initialized
+
+    // no dtor => stays a (static) local, initialized here
+    static const char bar = -1;
+
+    // has dtor => becomes a global, ctor called here, dtor called at exit
+    static const thing it;
+
+    printf("almost done\n");
 }
Index: tests/exceptions/cancel/.expect/thread.txt
===================================================================
--- tests/exceptions/cancel/.expect/thread.txt	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
+++ tests/exceptions/cancel/.expect/thread.txt	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -0,0 +1,2 @@
+0112345
+0112345
Index: tests/exceptions/cancel/coroutine.cfa
===================================================================
--- tests/exceptions/cancel/coroutine.cfa	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ tests/exceptions/cancel/coroutine.cfa	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -1,5 +1,4 @@
 // Try cancelling a coroutine.
 
-#include <stdio.h>
 #include <coroutine.hfa>
 #include <exception.hfa>
Index: tests/exceptions/cancel/thread.cfa
===================================================================
--- tests/exceptions/cancel/thread.cfa	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
+++ tests/exceptions/cancel/thread.cfa	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -0,0 +1,56 @@
+// Try cancelling a thread.
+
+#include <thread.hfa>
+#include <exception.hfa>
+
+TRIVIAL_EXCEPTION(internal_error);
+
+thread WillCancel {};
+
+const char * msg(ThreadCancelled(WillCancel) * this) {
+	return "ThreadCancelled(WillCancel)";
+}
+
+void main(WillCancel &) {
+	printf("1");
+	cancel_stack((internal_error){});
+	printf("!");
+}
+
+void explicit() {
+	try {
+		printf("0");
+		WillCancel cancel;
+		printf("1");
+		join(cancel);
+		printf("4");
+	} catchResume (ThreadCancelled(WillCancel) * error) {
+		printf("2");
+		if ((virtual internal_error *)error->the_exception) {
+			printf("3");
+		}
+	}
+	printf("5\n");
+}
+
+void implicit() {
+	try {
+		{
+			printf("0");
+			WillCancel cancel;
+			printf("1");
+		}
+		printf("4");
+	} catchResume (ThreadCancelled(WillCancel) * error) {
+		printf("2");
+		if ((virtual internal_error *)error->the_exception) {
+			printf("3");
+		}
+	}
+	printf("5\n");
+}
+
+int main(int argc, char * argv[]) {
+	explicit();
+	implicit();
+}
Index: tests/exceptions/conditional.cfa
===================================================================
--- tests/exceptions/conditional.cfa	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ tests/exceptions/conditional.cfa	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -5,5 +5,4 @@
 
 #include <exception.hfa>
-#include <stdio.h>
 
 VTABLE_DECLARATION(num_error)(
Index: tests/exceptions/except-io.hfa
===================================================================
--- tests/exceptions/except-io.hfa	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ tests/exceptions/except-io.hfa	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -1,5 +1,3 @@
 // Common tools for the exception tests.
-
-#include <stdio.h>
 
 // Echo when a destructor is run and an area/block is left.
Index: tests/exceptions/trash.cfa
===================================================================
--- tests/exceptions/trash.cfa	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ tests/exceptions/trash.cfa	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -2,5 +2,4 @@
 
 #include <exception.hfa>
-#include <stdio.h>
 
 TRIVIAL_EXCEPTION(yin);
Index: tests/global-monomorph.cfa
===================================================================
--- tests/global-monomorph.cfa	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ tests/global-monomorph.cfa	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -1,6 +1,3 @@
-// Crea
-
-#include <stdlib.hfa>
-#include <stdio.h>
+// Create monomorphic instances of polymorphic types at global scope.
 
 forall(dtype T)
Index: tests/poly-d-cycle.cfa
===================================================================
--- tests/poly-d-cycle.cfa	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ tests/poly-d-cycle.cfa	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -1,5 +1,3 @@
 // Check that a cycle of polymorphic dtype structures can be instancated.
-
-#include <stdio.h>
 
 forall(dtype T)
Index: tests/poly-o-cycle.cfa
===================================================================
--- tests/poly-o-cycle.cfa	(revision b9537e6e28ae020f54097b3d63e5a8bebeaf1cde)
+++ tests/poly-o-cycle.cfa	(revision f7e4f8e88c21cb43ef3af3a6e6bbdbba94f76af5)
@@ -1,5 +1,3 @@
 // Check that a cycle of polymorphic otype structures can be instancated.
-
-#include <stdio.h>
 
 forall(otype T)
