Index: src/AST/Convert.cpp
===================================================================
--- src/AST/Convert.cpp	(revision 0b0a285cb86eb63d47d96868ae2d0f99f753448c)
+++ src/AST/Convert.cpp	(revision c84dd616ea09c9d1bd0ef0c0fba904ff70c4d83d)
@@ -2343,5 +2343,6 @@
 				old->location,
 				GET_ACCEPT_1(arg, Expr),
-				old->isGenerated ? ast::GeneratedCast : ast::ExplicitCast
+				old->isGenerated ? ast::GeneratedCast : ast::ExplicitCast,
+				(ast::CastExpr::CastKind) old->kind
 			)
 		);
Index: src/AST/Expr.cpp
===================================================================
--- src/AST/Expr.cpp	(revision 0b0a285cb86eb63d47d96868ae2d0f99f753448c)
+++ src/AST/Expr.cpp	(revision c84dd616ea09c9d1bd0ef0c0fba904ff70c4d83d)
@@ -186,6 +186,6 @@
 // --- CastExpr
 
-CastExpr::CastExpr( const CodeLocation & loc, const Expr * a, GeneratedFlag g )
-: Expr( loc, new VoidType{} ), arg( a ), isGenerated( g ) {}
+CastExpr::CastExpr( const CodeLocation & loc, const Expr * a, GeneratedFlag g, CastKind kind )
+: Expr( loc, new VoidType{} ), arg( a ), isGenerated( g ), kind( kind ) {}
 
 bool CastExpr::get_lvalue() const {
Index: src/AST/Expr.hpp
===================================================================
--- src/AST/Expr.hpp	(revision 0b0a285cb86eb63d47d96868ae2d0f99f753448c)
+++ src/AST/Expr.hpp	(revision c84dd616ea09c9d1bd0ef0c0fba904ff70c4d83d)
@@ -55,4 +55,6 @@
 		const Expr * e )
 	: decl( id ), declptr( declptr ), actualType( actual ), formalType( formal ), expr( e ) {}
+
+	operator bool() {return declptr;}
 };
 
@@ -335,8 +337,16 @@
 	GeneratedFlag isGenerated;
 
+	enum CastKind {
+		Default, // C
+		Coerce, // reinterpret cast
+		Return  // overload selection
+	};
+
+	CastKind kind = Default;
+
 	CastExpr( const CodeLocation & loc, const Expr * a, const Type * to,
-		GeneratedFlag g = GeneratedCast ) : Expr( loc, to ), arg( a ), isGenerated( g ) {}
+		GeneratedFlag g = GeneratedCast, CastKind kind = Default ) : Expr( loc, to ), arg( a ), isGenerated( g ), kind( kind ) {}
 	/// Cast-to-void
-	CastExpr( const CodeLocation & loc, const Expr * a, GeneratedFlag g = GeneratedCast );
+	CastExpr( const CodeLocation & loc, const Expr * a, GeneratedFlag g = GeneratedCast, CastKind kind = Default );
 
 	/// Wrap a cast expression around an existing expression (always generated)
Index: src/AST/SymbolTable.cpp
===================================================================
--- src/AST/SymbolTable.cpp	(revision 0b0a285cb86eb63d47d96868ae2d0f99f753448c)
+++ src/AST/SymbolTable.cpp	(revision c84dd616ea09c9d1bd0ef0c0fba904ff70c4d83d)
@@ -19,4 +19,7 @@
 
 #include "Copy.hpp"
+#include <iostream>
+#include <algorithm>
+
 #include "Decl.hpp"
 #include "Expr.hpp"
@@ -203,4 +206,6 @@
 			out.push_back(decl.second);
 		}
+
+		// std::cerr << otypeKey << ' ' << out.size() << std::endl;
 	}
 
Index: src/AST/Type.hpp
===================================================================
--- src/AST/Type.hpp	(revision 0b0a285cb86eb63d47d96868ae2d0f99f753448c)
+++ src/AST/Type.hpp	(revision c84dd616ea09c9d1bd0ef0c0fba904ff70c4d83d)
@@ -451,5 +451,7 @@
 	bool operator==(const TypeEnvKey & other) const;
 	bool operator<(const TypeEnvKey & other) const;
-};
+	operator bool() {return base;}
+};
+
 
 /// tuple type e.g. `[int, char]`
Index: src/AST/TypeEnvironment.cpp
===================================================================
--- src/AST/TypeEnvironment.cpp	(revision 0b0a285cb86eb63d47d96868ae2d0f99f753448c)
+++ src/AST/TypeEnvironment.cpp	(revision c84dd616ea09c9d1bd0ef0c0fba904ff70c4d83d)
@@ -135,5 +135,5 @@
 		}
 	}
-	sub.normalize();
+	// sub.normalize();
 }
 
Index: src/AST/TypeEnvironment.hpp
===================================================================
--- src/AST/TypeEnvironment.hpp	(revision 0b0a285cb86eb63d47d96868ae2d0f99f753448c)
+++ src/AST/TypeEnvironment.hpp	(revision c84dd616ea09c9d1bd0ef0c0fba904ff70c4d83d)
@@ -63,5 +63,5 @@
 
 		int cmp = d1->var->name.compare( d2->var->name );
-		return cmp < 0 || ( cmp == 0 && d1->result < d2->result );
+		return cmp > 0 || ( cmp == 0 && d1->result < d2->result );
 	}
 };
Index: src/Concurrency/Actors.cpp
===================================================================
--- src/Concurrency/Actors.cpp	(revision 0b0a285cb86eb63d47d96868ae2d0f99f753448c)
+++ src/Concurrency/Actors.cpp	(revision c84dd616ea09c9d1bd0ef0c0fba904ff70c4d83d)
@@ -223,4 +223,86 @@
         if ( actorIter != actorStructDecls.end() && messageIter != messageStructDecls.end() ) {
             //////////////////////////////////////////////////////////////////////
+            // The following generates this wrapper for all receive(derived_actor &, derived_msg &) functions
+            /* base_actor and base_msg are output params
+            static inline allocation __CFA_receive_wrap( derived_actor & receiver, derived_msg & msg, actor ** base_actor, message ** base_msg ) {
+                base_actor = &receiver;
+                base_msg = &msg;
+                return receive( receiver, msg );
+            }
+            */
+            CompoundStmt * wrapBody = new CompoundStmt( decl->location );
+
+            // generates: base_actor = &receiver;
+            wrapBody->push_back( new ExprStmt( decl->location,
+                UntypedExpr::createAssign( decl->location, 
+                    UntypedExpr::createDeref( decl->location, new NameExpr( decl->location, "base_actor" ) ),
+                    new AddressExpr( decl->location, new NameExpr( decl->location, "receiver" ) )
+                )
+            ));
+
+            // generates: base_msg = &msg;
+            wrapBody->push_back( new ExprStmt( decl->location,
+                UntypedExpr::createAssign( decl->location, 
+                    UntypedExpr::createDeref( decl->location, new NameExpr( decl->location, "base_msg" ) ),
+                    new AddressExpr( decl->location, new NameExpr( decl->location, "msg" ) )
+                )
+            ));
+
+            // generates: return receive( receiver, msg );
+            wrapBody->push_back( new ReturnStmt( decl->location,
+                new UntypedExpr ( decl->location,
+                    new NameExpr( decl->location, "receive" ),
+                    {
+                        new NameExpr( decl->location, "receiver" ),
+                        new NameExpr( decl->location, "msg" )
+                    }
+                )
+            ));
+
+            // create receive wrapper to extract base message and actor pointer
+            // put it all together into the complete function decl from above
+            FunctionDecl * receiveWrapper = new FunctionDecl(
+                decl->location,
+                "__CFA_receive_wrap",
+                {},                     // forall
+                {
+                    new ObjectDecl(
+                        decl->location,
+                        "receiver",
+                        ast::deepCopy( derivedActorRef )
+                    ),
+                    new ObjectDecl(
+                        decl->location,
+                        "msg",
+                        ast::deepCopy( derivedMsgRef )
+                    ),
+                    new ObjectDecl(
+                        decl->location,
+                        "base_actor",
+                        new PointerType( new PointerType( new StructInstType( *actorDecl ) ) )
+                    ),
+                    new ObjectDecl(
+                        decl->location,
+                        "base_msg",
+                        new PointerType( new PointerType( new StructInstType( *msgDecl ) ) )
+                    )
+                },                      // params
+                { 
+                    new ObjectDecl(
+                        decl->location,
+                        "__CFA_receive_wrap_ret",
+                        new EnumInstType( *allocationDecl )
+                    )
+                },
+                wrapBody,               // body
+                { Storage::Static },    // storage
+                Linkage::Cforall,       // linkage
+                {},                     // attributes
+                { Function::Inline }
+            );
+
+            declsToAddAfter.push_back( receiveWrapper );
+
+            //////////////////////////////////////////////////////////////////////
             // The following generates this send message operator routine for all receive(derived_actor &, derived_msg &) functions
             /*
@@ -246,11 +328,13 @@
             ));
             
-            // Function type is: allocation (*)( derived_actor &, derived_msg & )
+            // Function type is: allocation (*)( derived_actor &, derived_msg &, actor **, message ** )
             FunctionType * derivedReceive = new FunctionType();
             derivedReceive->params.push_back( ast::deepCopy( derivedActorRef ) );
             derivedReceive->params.push_back( ast::deepCopy( derivedMsgRef ) );
+            derivedReceive->params.push_back( new PointerType( new PointerType( new StructInstType( *actorDecl ) ) ) );
+            derivedReceive->params.push_back( new PointerType( new PointerType( new StructInstType( *msgDecl ) ) ) );
             derivedReceive->returns.push_back( new EnumInstType( *allocationDecl ) );
 
-            // Generates: allocation (*my_work_fn)( derived_actor &, derived_msg & ) = receive;
+            // Generates: allocation (*my_work_fn)( derived_actor &, derived_msg &, actor **, message ** ) = receive;
             sendBody->push_back( new DeclStmt(
                 decl->location,
@@ -259,5 +343,5 @@
                     "my_work_fn",
                     new PointerType( derivedReceive ),
-                    new SingleInit( decl->location, new NameExpr( decl->location, "receive" ) )
+                    new SingleInit( decl->location, new NameExpr( decl->location, "__CFA_receive_wrap" ) )
                 )
             ));
@@ -267,4 +351,6 @@
             genericReceive->params.push_back( new ReferenceType( new StructInstType( *actorDecl ) ) );
             genericReceive->params.push_back( new ReferenceType( new StructInstType( *msgDecl ) ) );
+            genericReceive->params.push_back( new PointerType( new PointerType( new StructInstType( *actorDecl ) ) ) );
+            genericReceive->params.push_back( new PointerType( new PointerType( new StructInstType( *msgDecl ) ) ) );
             genericReceive->returns.push_back( new EnumInstType( *allocationDecl ) );
 
@@ -285,5 +371,5 @@
             ));
 
-            // Generates: new_req{ &receiver, &msg, fn };
+            // Generates: new_req{ (actor *)&receiver, (message *)&msg, fn };
             sendBody->push_back( new ExprStmt(
                 decl->location,
@@ -293,6 +379,6 @@
 					{
 						new NameExpr( decl->location, "new_req" ),
-                        new AddressExpr( new NameExpr( decl->location, "receiver" ) ),
-                        new AddressExpr( new NameExpr( decl->location, "msg" ) ),
+                        new CastExpr( decl->location, new AddressExpr( new NameExpr( decl->location, "receiver" ) ), new PointerType( new StructInstType( *actorDecl ) ), ExplicitCast ),
+                        new CastExpr( decl->location, new AddressExpr( new NameExpr( decl->location, "msg" ) ), new PointerType( new StructInstType( *msgDecl ) ), ExplicitCast ),
                         new NameExpr( decl->location, "fn" )
 					}
@@ -321,5 +407,5 @@
             FunctionDecl * sendOperatorFunction = new FunctionDecl(
                 decl->location,
-                "?<<?",
+                "?|?",
                 {},                     // forall
                 {
Index: src/GenPoly/SpecializeNew.cpp
===================================================================
--- src/GenPoly/SpecializeNew.cpp	(revision 0b0a285cb86eb63d47d96868ae2d0f99f753448c)
+++ src/GenPoly/SpecializeNew.cpp	(revision c84dd616ea09c9d1bd0ef0c0fba904ff70c4d83d)
@@ -113,7 +113,8 @@
 	using namespace ResolvExpr;
 	ast::OpenVarSet openVars, closedVars;
-	ast::AssertionSet need, have;
-	findOpenVars( formalType, openVars, closedVars, need, have, FirstClosed );
-	findOpenVars( actualType, openVars, closedVars, need, have, FirstOpen );
+	ast::AssertionSet need, have; // unused
+	ast::TypeEnvironment env; // unused
+	// findOpenVars( formalType, openVars, closedVars, need, have, FirstClosed );
+	findOpenVars( actualType, openVars, closedVars, need, have, env, FirstOpen );
 	for ( const ast::OpenVarSet::value_type & openVar : openVars ) {
 		const ast::Type * boundType = subs->lookup( openVar.first );
@@ -125,4 +126,7 @@
 			if ( closedVars.find( *inst ) == closedVars.end() ) {
 				return true;
+			}
+			else {
+				assertf(false, "closed: %s", inst->name.c_str());
 			}
 		// Otherwise, the variable is bound to a concrete type.
Index: src/Parser/DeclarationNode.cc
===================================================================
--- src/Parser/DeclarationNode.cc	(revision 0b0a285cb86eb63d47d96868ae2d0f99f753448c)
+++ src/Parser/DeclarationNode.cc	(revision c84dd616ea09c9d1bd0ef0c0fba904ff70c4d83d)
@@ -9,7 +9,7 @@
 // Author           : Rodolfo G. Esteves
 // Created On       : Sat May 16 12:34:05 2015
-// Last Modified By : Andrew Beach
-// Last Modified On : Thr Apr 20 11:46:00 2023
-// Update Count     : 1393
+// Last Modified By : Peter A. Buhr
+// Last Modified On : Sat Jun 17 14:41:48 2023
+// Update Count     : 1405
 //
 
@@ -459,6 +459,5 @@
 	std::vector<ast::ptr<ast::Expr>> exprs;
 	buildList( expr, exprs );
-	newnode->attributes.push_back(
-		new ast::Attribute( *name, std::move( exprs ) ) );
+	newnode->attributes.push_back( new ast::Attribute( *name, std::move( exprs ) ) );
 	delete name;
 	return newnode;
@@ -633,15 +632,18 @@
 					dst->basictype = src->basictype;
 				} else if ( src->basictype != DeclarationNode::NoBasicType )
-					SemanticError( yylloc, src, string( "conflicting type specifier " ) + DeclarationNode::basicTypeNames[ src->basictype ] + " in type: " );
+					SemanticError( yylloc, string( "multiple declaration types \"" ) + DeclarationNode::basicTypeNames[ dst->basictype ] +
+								   "\" and \"" + DeclarationNode::basicTypeNames[ src->basictype ] + "\"." );
 
 				if ( dst->complextype == DeclarationNode::NoComplexType ) {
 					dst->complextype = src->complextype;
 				} else if ( src->complextype != DeclarationNode::NoComplexType )
-					SemanticError( yylloc, src, string( "conflicting type specifier " ) + DeclarationNode::complexTypeNames[ src->complextype ] + " in type: " );
+					SemanticError( yylloc, string( "multiple declaration types \"" ) + DeclarationNode::complexTypeNames[ src->complextype ] +
+								   "\" and \"" + DeclarationNode::complexTypeNames[ src->complextype ] + "\"." );
 
 				if ( dst->signedness == DeclarationNode::NoSignedness ) {
 					dst->signedness = src->signedness;
 				} else if ( src->signedness != DeclarationNode::NoSignedness )
-					SemanticError( yylloc, src, string( "conflicting type specifier " ) + DeclarationNode::signednessNames[ src->signedness ] + " in type: " );
+					SemanticError( yylloc, string( "conflicting type specifier \"" ) + DeclarationNode::signednessNames[ dst->signedness ] +
+								   "\" and \"" + DeclarationNode::signednessNames[ src->signedness ] + "\"." );
 
 				if ( dst->length == DeclarationNode::NoLength ) {
@@ -650,5 +652,6 @@
 					dst->length = DeclarationNode::LongLong;
 				} else if ( src->length != DeclarationNode::NoLength )
-					SemanticError( yylloc, src, string( "conflicting type specifier " ) + DeclarationNode::lengthNames[ src->length ] + " in type: " );
+					SemanticError( yylloc, string( "conflicting type specifier \"" ) + DeclarationNode::lengthNames[ dst->length ] +
+								   "\" and \"" + DeclarationNode::lengthNames[ src->length ] + "\"." );
 			} // if
 			break;
@@ -718,7 +721,7 @@
 
 DeclarationNode * DeclarationNode::addEnumBase( DeclarationNode * o ) {
-	if ( o && o -> type)  {
+	if ( o && o->type)  {
 		type->base= o->type;
-	}
+	} // if
 	delete o;
 	return this;
@@ -1003,6 +1006,6 @@
 }
 
-// If a typedef wraps an anonymous declaration, name the inner declaration
-// so it has a consistent name across translation units.
+// If a typedef wraps an anonymous declaration, name the inner declaration so it has a consistent name across
+// translation units.
 static void nameTypedefedDecl(
 		DeclarationNode * innerDecl,
@@ -1085,6 +1088,5 @@
 }
 
-void buildList( DeclarationNode * firstNode,
-		std::vector<ast::ptr<ast::Decl>> & outputList ) {
+void buildList( DeclarationNode * firstNode, std::vector<ast::ptr<ast::Decl>> & outputList ) {
 	SemanticErrorException errors;
 	std::back_insert_iterator<std::vector<ast::ptr<ast::Decl>>> out( outputList );
Index: src/Parser/ExpressionNode.cc
===================================================================
--- src/Parser/ExpressionNode.cc	(revision 0b0a285cb86eb63d47d96868ae2d0f99f753448c)
+++ src/Parser/ExpressionNode.cc	(revision c84dd616ea09c9d1bd0ef0c0fba904ff70c4d83d)
@@ -601,5 +601,6 @@
 ast::Expr * build_cast( const CodeLocation & location,
 		DeclarationNode * decl_node,
-		ExpressionNode * expr_node ) {
+		ExpressionNode * expr_node,
+		ast::CastExpr::CastKind kind ) {
 	ast::Type * targetType = maybeMoveBuildType( decl_node );
 	if ( dynamic_cast<ast::VoidType *>( targetType ) ) {
@@ -607,10 +608,10 @@
 		return new ast::CastExpr( location,
 			maybeMoveBuild( expr_node ),
-			ast::ExplicitCast );
+			ast::ExplicitCast, kind );
 	} else {
 		return new ast::CastExpr( location,
 			maybeMoveBuild( expr_node ),
 			targetType,
-			ast::ExplicitCast );
+			ast::ExplicitCast, kind );
 	} // if
 } // build_cast
Index: src/Parser/ExpressionNode.h
===================================================================
--- src/Parser/ExpressionNode.h	(revision 0b0a285cb86eb63d47d96868ae2d0f99f753448c)
+++ src/Parser/ExpressionNode.h	(revision c84dd616ea09c9d1bd0ef0c0fba904ff70c4d83d)
@@ -69,5 +69,5 @@
 ast::DimensionExpr * build_dimensionref( const CodeLocation &, const std::string * name );
 
-ast::Expr * build_cast( const CodeLocation &, DeclarationNode * decl_node, ExpressionNode * expr_node );
+ast::Expr * build_cast( const CodeLocation &, DeclarationNode * decl_node, ExpressionNode * expr_node, ast::CastExpr::CastKind kind = ast::CastExpr::Default );
 ast::Expr * build_keyword_cast( const CodeLocation &, ast::AggregateDecl::Aggregate target, ExpressionNode * expr_node );
 ast::Expr * build_virtual_cast( const CodeLocation &, DeclarationNode * decl_node, ExpressionNode * expr_node );
Index: src/Parser/parser.yy
===================================================================
--- src/Parser/parser.yy	(revision 0b0a285cb86eb63d47d96868ae2d0f99f753448c)
+++ src/Parser/parser.yy	(revision c84dd616ea09c9d1bd0ef0c0fba904ff70c4d83d)
@@ -10,6 +10,6 @@
 // Created On       : Sat Sep  1 20:22:55 2001
 // Last Modified By : Peter A. Buhr
-// Last Modified On : Wed Jun  7 14:32:28 2023
-// Update Count     : 6341
+// Last Modified On : Sat Jun 17 18:53:24 2023
+// Update Count     : 6347
 //
 
@@ -931,5 +931,5 @@
 		{ $$ = new ExpressionNode( new ast::VirtualCastExpr( yylloc, maybeMoveBuild( $5 ), maybeMoveBuildType( $3 ) ) ); }
 	| '(' RETURN type_no_function ')' cast_expression	// CFA
-		{ SemanticError( yylloc, "Return cast is currently unimplemented." ); $$ = nullptr; }
+		{ $$ = new ExpressionNode( build_cast( yylloc, $3, $5, ast::CastExpr::Return ) ); }
 	| '(' COERCE type_no_function ')' cast_expression	// CFA
 		{ SemanticError( yylloc, "Coerce cast is currently unimplemented." ); $$ = nullptr; }
@@ -1040,5 +1040,5 @@
 		// FIX ME: computes $1 twice
 	| logical_OR_expression '?' /* empty */ ':' conditional_expression // GCC, omitted first operand
-		{ $$ = new ExpressionNode( build_cond( yylloc, $1, $1, $4 ) ); }
+		{ $$ = new ExpressionNode( build_cond( yylloc, $1, $1->clone(), $4 ) ); }
 	;
 
Index: src/ResolvExpr/Candidate.hpp
===================================================================
--- src/ResolvExpr/Candidate.hpp	(revision 0b0a285cb86eb63d47d96868ae2d0f99f753448c)
+++ src/ResolvExpr/Candidate.hpp	(revision c84dd616ea09c9d1bd0ef0c0fba904ff70c4d83d)
@@ -91,4 +91,5 @@
 
 /// Holdover behaviour from old `findMinCost` -- xxx -- can maybe be eliminated?
+/*
 static inline void promoteCvtCost( CandidateList & candidates ) {
 	for ( CandidateRef & r : candidates ) {
@@ -96,4 +97,5 @@
 	}
 }
+*/
 
 void print( std::ostream & os, const Candidate & cand, Indenter indent = {} );
Index: src/ResolvExpr/CandidateFinder.cpp
===================================================================
--- src/ResolvExpr/CandidateFinder.cpp	(revision 0b0a285cb86eb63d47d96868ae2d0f99f753448c)
+++ src/ResolvExpr/CandidateFinder.cpp	(revision c84dd616ea09c9d1bd0ef0c0fba904ff70c4d83d)
@@ -38,4 +38,5 @@
 #include "typeops.h"              // for combos
 #include "Unify.h"
+#include "WidenMode.h"
 #include "AST/Expr.hpp"
 #include "AST/Node.hpp"
@@ -749,9 +750,19 @@
 			// attempt to narrow based on expected target type
 			const ast::Type * returnType = funcType->returns.front();
-			if ( ! unify(
-				returnType, targetType, funcEnv, funcNeed, funcHave, funcOpen )
-			) {
-				// unification failed, do not pursue this candidate
-				return;
+			if ( selfFinder.strictMode ) {
+				if ( ! unifyExact(
+					returnType, targetType, funcEnv, funcNeed, funcHave, funcOpen, noWiden() ) // xxx - is no widening correct?
+				) {
+					// unification failed, do not pursue this candidate
+					return;
+				}
+			}
+			else {
+				if ( ! unify(
+					returnType, targetType, funcEnv, funcNeed, funcHave, funcOpen )
+				) {
+					// unification failed, do not pursue this candidate
+					return;
+				}
 			}
 		}
@@ -771,5 +782,5 @@
 				for (size_t i=0; i<nParams; ++i) {
 					auto obj = funcDecl->params[i].strict_as<ast::ObjectDecl>();
-					if (!instantiateArgument( location,
+					if ( !instantiateArgument( location,
 						funcType->params[i], obj->init, args, results, genStart, symtab)) return;
 				}
@@ -781,5 +792,5 @@
 			// matches
 			// no default args for indirect calls
-			if ( ! instantiateArgument( location,
+			if ( !instantiateArgument( location,
 				param, nullptr, args, results, genStart, symtab ) ) return;
 		}
@@ -874,7 +885,7 @@
 
 		if ( auto structInst = aggrExpr->result.as< ast::StructInstType >() ) {
-			addAggMembers( structInst, aggrExpr, *cand, Cost::safe, "" );
+			addAggMembers( structInst, aggrExpr, *cand, Cost::unsafe, "" );
 		} else if ( auto unionInst = aggrExpr->result.as< ast::UnionInstType >() ) {
-			addAggMembers( unionInst, aggrExpr, *cand, Cost::safe, "" );
+			addAggMembers( unionInst, aggrExpr, *cand, Cost::unsafe, "" );
 		}
 	}
@@ -1007,4 +1018,5 @@
 				if ( auto pointer = dynamic_cast< const ast::PointerType * >( funcResult ) ) {
 					if ( auto function = pointer->base.as< ast::FunctionType >() ) {
+						// if (!selfFinder.allowVoid && function->returns.empty()) continue;
 						CandidateRef newFunc{ new Candidate{ *func } };
 						newFunc->expr =
@@ -1018,5 +1030,5 @@
 					if ( const ast::EqvClass * clz = func->env.lookup( *inst ) ) {
 						if ( auto function = clz->bound.as< ast::FunctionType >() ) {
-							CandidateRef newFunc{ new Candidate{ *func } };
+							CandidateRef newFunc( new Candidate( *func ) );
 							newFunc->expr =
 								referenceToRvalueConversion( newFunc->expr, newFunc->cost );
@@ -1060,4 +1072,9 @@
 		if ( found.empty() && ! errors.isEmpty() ) { throw errors; }
 
+		// only keep the best matching intrinsic result to match C semantics (no unexpected narrowing/widening)
+		// TODO: keep one for each set of argument candidates?
+		Cost intrinsicCost = Cost::infinity;
+		CandidateList intrinsicResult;
+
 		// Compute conversion costs
 		for ( CandidateRef & withFunc : found ) {
@@ -1082,22 +1099,37 @@
 			if ( cvtCost != Cost::infinity ) {
 				withFunc->cvtCost = cvtCost;
-				candidates.emplace_back( std::move( withFunc ) );
-			}
-		}
+				withFunc->cost += cvtCost;
+				auto func = withFunc->expr.strict_as<ast::ApplicationExpr>()->func.as<ast::VariableExpr>();
+				if (func && func->var->linkage == ast::Linkage::Intrinsic) {
+					if (withFunc->cost < intrinsicCost) {
+						intrinsicResult.clear();
+						intrinsicCost = withFunc->cost;
+					}
+					if (withFunc->cost == intrinsicCost) {
+						intrinsicResult.emplace_back(std::move(withFunc));
+					}
+				}
+				else {
+					candidates.emplace_back( std::move( withFunc ) );
+				}
+			}
+		}
+		spliceBegin( candidates, intrinsicResult );
 		found = std::move( candidates );
 
 		// use a new list so that candidates are not examined by addAnonConversions twice
-		CandidateList winners = findMinCost( found );
-		promoteCvtCost( winners );
+		// CandidateList winners = findMinCost( found );
+		// promoteCvtCost( winners );
 
 		// function may return a struct/union value, in which case we need to add candidates
 		// for implicit conversions to each of the anonymous members, which must happen after
 		// `findMinCost`, since anon conversions are never the cheapest
-		for ( const CandidateRef & c : winners ) {
+		for ( const CandidateRef & c : found ) {
 			addAnonConversions( c );
 		}
-		spliceBegin( candidates, winners );
-
-		if ( candidates.empty() && targetType && ! targetType->isVoid() ) {
+		// would this be too slow when we don't check cost anymore?
+		spliceBegin( candidates, found );
+
+		if ( candidates.empty() && targetType && ! targetType->isVoid() && !selfFinder.strictMode ) {
 			// If resolution is unsuccessful with a target type, try again without, since it
 			// will sometimes succeed when it wouldn't with a target type binding.
@@ -1140,4 +1172,14 @@
 
 		CandidateFinder finder( context, tenv, toType );
+		if (toType->isVoid()) {
+			finder.allowVoid = true;
+		}
+		if ( castExpr->kind == ast::CastExpr::Return ) {
+			finder.strictMode = true;
+			finder.find( castExpr->arg, ResolvMode::withAdjustment() );
+
+			// return casts are eliminated (merely selecting an overload, no actual operation)
+			candidates = std::move(finder.candidates);
+		}
 		finder.find( castExpr->arg, ResolvMode::withAdjustment() );
 
@@ -1145,4 +1187,6 @@
 
 		CandidateList matches;
+		Cost minExprCost = Cost::infinity;
+		Cost minCastCost = Cost::infinity;
 		for ( CandidateRef & cand : finder.candidates ) {
 			ast::AssertionSet need( cand->need.begin(), cand->need.end() ), have;
@@ -1176,16 +1220,31 @@
 				// count one safe conversion for each value that is thrown away
 				thisCost.incSafe( discardedValues );
-				CandidateRef newCand = std::make_shared<Candidate>(
-					restructureCast( cand->expr, toType, castExpr->isGenerated ),
-					copy( cand->env ), std::move( open ), std::move( need ), cand->cost,
-					cand->cost + thisCost );
-				inferParameters( newCand, matches );
-			}
-		}
-
-		// select first on argument cost, then conversion cost
-		CandidateList minArgCost = findMinCost( matches );
-		promoteCvtCost( minArgCost );
-		candidates = findMinCost( minArgCost );
+				// select first on argument cost, then conversion cost
+				if ( cand->cost < minExprCost || ( cand->cost == minExprCost && thisCost < minCastCost ) ) {
+					minExprCost = cand->cost;
+					minCastCost = thisCost;
+					matches.clear();
+
+
+				}
+				// ambiguous case, still output candidates to print in error message
+				if ( cand->cost == minExprCost && thisCost == minCastCost ) {
+					CandidateRef newCand = std::make_shared<Candidate>(
+						restructureCast( cand->expr, toType, castExpr->isGenerated ),
+						copy( cand->env ), std::move( open ), std::move( need ), cand->cost + thisCost);
+					// currently assertions are always resolved immediately so this should have no effect. 
+					// if this somehow changes in the future (e.g. delayed by indeterminate return type)
+					// we may need to revisit the logic.
+					inferParameters( newCand, matches );
+				}
+				// else skip, better alternatives found
+
+			}
+		}
+		candidates = std::move(matches);
+
+		//CandidateList minArgCost = findMinCost( matches );
+		//promoteCvtCost( minArgCost );
+		//candidates = findMinCost( minArgCost );
 	}
 
@@ -1453,4 +1512,5 @@
 		// candidates for true result
 		CandidateFinder finder2( context, tenv );
+		finder2.allowVoid = true;
 		finder2.find( conditionalExpr->arg2, ResolvMode::withAdjustment() );
 		if ( finder2.candidates.empty() ) return;
@@ -1458,4 +1518,5 @@
 		// candidates for false result
 		CandidateFinder finder3( context, tenv );
+		finder3.allowVoid = true;
 		finder3.find( conditionalExpr->arg3, ResolvMode::withAdjustment() );
 		if ( finder3.candidates.empty() ) return;
@@ -1524,4 +1585,5 @@
 	void Finder::postvisit( const ast::ConstructorExpr * ctorExpr ) {
 		CandidateFinder finder( context, tenv );
+		finder.allowVoid = true;
 		finder.find( ctorExpr->callExpr, ResolvMode::withoutPrune() );
 		for ( CandidateRef & r : finder.candidates ) {
@@ -1640,4 +1702,7 @@
 			CandidateFinder finder( context, tenv, toType );
 			finder.find( initExpr->expr, ResolvMode::withAdjustment() );
+
+			Cost minExprCost = Cost::infinity;
+			Cost minCastCost = Cost::infinity;
 			for ( CandidateRef & cand : finder.candidates ) {
 				if (reason.code == NotFound) reason.code = NoMatch;
@@ -1677,10 +1742,22 @@
 					// count one safe conversion for each value that is thrown away
 					thisCost.incSafe( discardedValues );
-					CandidateRef newCand = std::make_shared<Candidate>(
-						new ast::InitExpr{
-							initExpr->location, restructureCast( cand->expr, toType ),
-							initAlt.designation },
-						std::move(env), std::move( open ), std::move( need ), cand->cost, thisCost );
-					inferParameters( newCand, matches );
+					if ( cand->cost < minExprCost || ( cand->cost == minExprCost && thisCost < minCastCost ) ) {
+						minExprCost = cand->cost;
+						minCastCost = thisCost;
+						matches.clear();
+					}
+					// ambiguous case, still output candidates to print in error message
+					if ( cand->cost == minExprCost && thisCost == minCastCost ) {
+						CandidateRef newCand = std::make_shared<Candidate>(
+							new ast::InitExpr{
+								initExpr->location,
+								restructureCast( cand->expr, toType ),
+								initAlt.designation },
+							std::move(env), std::move( open ), std::move( need ), cand->cost + thisCost );
+						// currently assertions are always resolved immediately so this should have no effect.
+						// if this somehow changes in the future (e.g. delayed by indeterminate return type)
+						// we may need to revisit the logic.
+						inferParameters( newCand, matches );
+					}
 				}
 			}
@@ -1688,7 +1765,8 @@
 
 		// select first on argument cost, then conversion cost
-		CandidateList minArgCost = findMinCost( matches );
-		promoteCvtCost( minArgCost );
-		candidates = findMinCost( minArgCost );
+		// CandidateList minArgCost = findMinCost( matches );
+		// promoteCvtCost( minArgCost );
+		// candidates = findMinCost( minArgCost );
+		candidates = std::move(matches);
 	}
 
@@ -1756,5 +1834,10 @@
 			auto found = selected.find( mangleName );
 			if ( found != selected.end() ) {
-				if ( newCand->cost < found->second.candidate->cost ) {
+				// tiebreaking by picking the lower cost on CURRENT expression
+				// NOTE: this behavior is different from C semantics.
+				// Specific remediations are performed for C operators at postvisit(UntypedExpr).
+				// Further investigations may take place.
+				if ( newCand->cost < found->second.candidate->cost
+					|| (newCand->cost == found->second.candidate->cost && newCand->cvtCost < found->second.candidate->cvtCost) ) {
 					PRINT(
 						std::cerr << "cost " << newCand->cost << " beats "
@@ -1763,5 +1846,5 @@
 
 					found->second = PruneStruct{ newCand };
-				} else if ( newCand->cost == found->second.candidate->cost ) {
+				} else if ( newCand->cost == found->second.candidate->cost && newCand->cvtCost == found->second.candidate->cvtCost ) {
 					// if one of the candidates contains a deleted identifier, can pick the other,
 					// since deleted expressions should not be ambiguous if there is another option
@@ -1854,5 +1937,6 @@
 	*/
 
-	if ( mode.prune ) {
+	// optimization: don't prune for NameExpr since it never has cost
+	if ( mode.prune && !dynamic_cast<const ast::NameExpr *>(expr) ) {
 		// trim candidates to single best one
 		PRINT(
Index: src/ResolvExpr/CandidateFinder.hpp
===================================================================
--- src/ResolvExpr/CandidateFinder.hpp	(revision 0b0a285cb86eb63d47d96868ae2d0f99f753448c)
+++ src/ResolvExpr/CandidateFinder.hpp	(revision c84dd616ea09c9d1bd0ef0c0fba904ff70c4d83d)
@@ -33,4 +33,6 @@
 	const ast::TypeEnvironment & env;  ///< Substitutions performed in this resolution
 	ast::ptr< ast::Type > targetType;  ///< Target type for resolution
+	bool strictMode = false;           ///< If set to true, requires targetType to be exact match (inside return cast)
+	bool allowVoid = false;            ///< If set to true, allow void-returning function calls (only top level, cast to void and first in comma)
 	std::set< std::string > otypeKeys;  /// different type may map to same key
 
Index: src/ResolvExpr/CastCost.cc
===================================================================
--- src/ResolvExpr/CastCost.cc	(revision 0b0a285cb86eb63d47d96868ae2d0f99f753448c)
+++ src/ResolvExpr/CastCost.cc	(revision c84dd616ea09c9d1bd0ef0c0fba904ff70c4d83d)
@@ -234,4 +234,7 @@
 	if ( typesCompatibleIgnoreQualifiers( src, dst, env ) ) {
 		PRINT( std::cerr << "compatible!" << std::endl; )
+		if (dynamic_cast<const ast::ZeroType *>(dst) || dynamic_cast<const ast::OneType *>(dst)) {
+			return Cost::spec;
+		}
 		return Cost::zero;
 	} else if ( dynamic_cast< const ast::VoidType * >( dst ) ) {
Index: src/ResolvExpr/CommonType.cc
===================================================================
--- src/ResolvExpr/CommonType.cc	(revision 0b0a285cb86eb63d47d96868ae2d0f99f753448c)
+++ src/ResolvExpr/CommonType.cc	(revision c84dd616ea09c9d1bd0ef0c0fba904ff70c4d83d)
@@ -697,4 +697,6 @@
 			if ( auto basic2 = dynamic_cast< const ast::BasicType * >( type2 ) ) {
 				#warning remove casts when `commonTypes` moved to new AST
+				
+				/*
 				ast::BasicType::Kind kind = (ast::BasicType::Kind)(int)commonTypes[ (BasicType::Kind)(int)basic->kind ][ (BasicType::Kind)(int)basic2->kind ];
 				if (
@@ -706,4 +708,16 @@
 					result = new ast::BasicType{ kind, basic->qualifiers | basic2->qualifiers };
 				}
+				*/
+				ast::BasicType::Kind kind;
+				if (basic->kind != basic2->kind && !widen.first && !widen.second) return;
+				else if (!widen.first) kind = basic->kind; // widen.second
+				else if (!widen.second) kind = basic2->kind;
+				else kind = (ast::BasicType::Kind)(int)commonTypes[ (BasicType::Kind)(int)basic->kind ][ (BasicType::Kind)(int)basic2->kind ];
+				// xxx - what does qualifiers even do here??
+				if ( (basic->qualifiers >= basic2->qualifiers || widen.first)
+					&& (basic->qualifiers <= basic2->qualifiers || widen.second) ) {
+					result = new ast::BasicType{ kind, basic->qualifiers | basic2->qualifiers };
+				}
+				
 			} else if (
 				dynamic_cast< const ast::ZeroType * >( type2 )
@@ -712,11 +726,14 @@
 				#warning remove casts when `commonTypes` moved to new AST
 				ast::BasicType::Kind kind = (ast::BasicType::Kind)(int)commonTypes[ (BasicType::Kind)(int)basic->kind ][ (BasicType::Kind)(int)ast::BasicType::SignedInt ];
-				if (
-					( ( kind == basic->kind && basic->qualifiers >= type2->qualifiers )
+				/*
+				if ( // xxx - what does qualifier even do here??
+					( ( basic->qualifiers >= type2->qualifiers )
 						|| widen.first )
-					&& ( ( kind != basic->kind && basic->qualifiers <= type2->qualifiers )
+					 && ( ( /* kind != basic->kind && basic->qualifiers <= type2->qualifiers )
 						|| widen.second )
-				) {
-					result = new ast::BasicType{ kind, basic->qualifiers | type2->qualifiers };
+				) 
+				*/
+				if (widen.second) {
+					result = new ast::BasicType{ basic->kind, basic->qualifiers | type2->qualifiers };
 				}
 			} else if ( const ast::EnumInstType * enumInst = dynamic_cast< const ast::EnumInstType * >( type2 ) ) {
@@ -746,4 +763,5 @@
 				auto entry = open.find( *var );
 				if ( entry != open.end() ) {
+				// if (tenv.lookup(*var)) {
 					ast::AssertionSet need, have;
 					if ( ! tenv.bindVar(
Index: src/ResolvExpr/ConversionCost.cc
===================================================================
--- src/ResolvExpr/ConversionCost.cc	(revision 0b0a285cb86eb63d47d96868ae2d0f99f753448c)
+++ src/ResolvExpr/ConversionCost.cc	(revision c84dd616ea09c9d1bd0ef0c0fba904ff70c4d83d)
@@ -702,4 +702,7 @@
 
 	cost = costCalc( refType->base, dst, srcIsLvalue, symtab, env );
+
+	// xxx - should qualifiers be considered in pass-by-value?
+	/*
 	if ( refType->base->qualifiers == dst->qualifiers ) {
 		cost.incReference();
@@ -709,4 +712,6 @@
 		cost.incUnsafe();
 	}
+	*/
+	cost.incReference();
 }
 
@@ -792,8 +797,14 @@
 			cost.incSign( signMatrix[ ast::BasicType::SignedInt ][ dstAsBasic->kind ] );
 		}
+		// this has the effect of letting any expr such as x+0, x+1 to be typed
+		// the same as x, instead of at least int. are we willing to sacrifice this little
+		// bit of coherence with C?
+		// TODO: currently this does not work when no zero/one overloads exist. Find a fix for it.
+		// cost = Cost::zero;
 	} else if ( dynamic_cast< const ast::PointerType * >( dst ) ) {
 		cost = Cost::zero;
 		// +1 for zero_t ->, +1 for disambiguation
 		cost.incSafe( maxIntCost + 2 );
+		// assuming 0p is supposed to be used for pointers?
 	}
 }
@@ -804,5 +815,5 @@
 		cost = Cost::zero;
 	} else if ( const ast::BasicType * dstAsBasic =
-			dynamic_cast< const ast::BasicType * >( dst ) ) {
+			dynamic_cast< const ast::BasicType * >( dst ) ) {		
 		int tableResult = costMatrix[ ast::BasicType::SignedInt ][ dstAsBasic->kind ];
 		if ( -1 == tableResult ) {
@@ -813,4 +824,6 @@
 			cost.incSign( signMatrix[ ast::BasicType::SignedInt ][ dstAsBasic->kind ] );
 		}
+		
+		// cost = Cost::zero;
 	}
 }
Index: src/ResolvExpr/FindOpenVars.cc
===================================================================
--- src/ResolvExpr/FindOpenVars.cc	(revision 0b0a285cb86eb63d47d96868ae2d0f99f753448c)
+++ src/ResolvExpr/FindOpenVars.cc	(revision c84dd616ea09c9d1bd0ef0c0fba904ff70c4d83d)
@@ -21,7 +21,10 @@
 #include "AST/Pass.hpp"
 #include "AST/Type.hpp"
+#include "AST/TypeEnvironment.hpp"
 #include "Common/PassVisitor.h"
 #include "SynTree/Declaration.h"  // for TypeDecl, DeclarationWithType (ptr ...
 #include "SynTree/Type.h"         // for Type, Type::ForallList, ArrayType
+
+#include <iostream>
 
 namespace ResolvExpr {
@@ -102,14 +105,21 @@
 			ast::AssertionSet & need;
 			ast::AssertionSet & have;
+			ast::TypeEnvironment & env;
 			bool nextIsOpen;
 
 			FindOpenVars_new(
 				ast::OpenVarSet & o, ast::OpenVarSet & c, ast::AssertionSet & n,
-				ast::AssertionSet & h, FirstMode firstIsOpen )
-			: open( o ), closed( c ), need( n ), have( h ), nextIsOpen( firstIsOpen ) {}
+				ast::AssertionSet & h, ast::TypeEnvironment & env, FirstMode firstIsOpen )
+			: open( o ), closed( c ), need( n ), have( h ), env (env), nextIsOpen( firstIsOpen ) {}
 
 			void previsit( const ast::FunctionType * type ) {
 				// mark open/closed variables
 				if ( nextIsOpen ) {
+					// trying to remove this from resolver.
+					// occasionally used in other parts so not deleting right now.
+
+					// insert open variables unbound to environment.
+					env.add(type->forall);
+
 					for ( auto & decl : type->forall ) {
 						open[ *decl ] = ast::TypeData{ decl->base };
@@ -137,7 +147,15 @@
 	void findOpenVars(
 			const ast::Type * type, ast::OpenVarSet & open, ast::OpenVarSet & closed,
-			ast::AssertionSet & need, ast::AssertionSet & have, FirstMode firstIsOpen ) {
-		ast::Pass< FindOpenVars_new > finder{ open, closed, need, have, firstIsOpen };
+			ast::AssertionSet & need, ast::AssertionSet & have, ast::TypeEnvironment & env, FirstMode firstIsOpen ) {
+		ast::Pass< FindOpenVars_new > finder{ open, closed, need, have, env, firstIsOpen };
 		type->accept( finder );
+
+		if (!closed.empty()) {
+			std::cerr << "closed: ";
+			for (auto& i : closed) {
+				std::cerr << i.first.base->location << ":" << i.first.base->name << ' ';
+			}
+			std::cerr << std::endl;
+		}
 	}
 } // namespace ResolvExpr
Index: src/ResolvExpr/FindOpenVars.h
===================================================================
--- src/ResolvExpr/FindOpenVars.h	(revision 0b0a285cb86eb63d47d96868ae2d0f99f753448c)
+++ src/ResolvExpr/FindOpenVars.h	(revision c84dd616ea09c9d1bd0ef0c0fba904ff70c4d83d)
@@ -33,5 +33,5 @@
 	void findOpenVars( 
 		const ast::Type * type, ast::OpenVarSet & open, ast::OpenVarSet & closed, 
-		ast::AssertionSet & need, ast::AssertionSet & have, FirstMode firstIsOpen );
+		ast::AssertionSet & need, ast::AssertionSet & have, ast::TypeEnvironment & env, FirstMode firstIsOpen );
 } // namespace ResolvExpr
 
Index: src/ResolvExpr/Resolver.cc
===================================================================
--- src/ResolvExpr/Resolver.cc	(revision 0b0a285cb86eb63d47d96868ae2d0f99f753448c)
+++ src/ResolvExpr/Resolver.cc	(revision c84dd616ea09c9d1bd0ef0c0fba904ff70c4d83d)
@@ -1011,4 +1011,5 @@
 			ast::TypeEnvironment env;
 			CandidateFinder finder( context, env );
+			finder.allowVoid = true;
 			finder.find( untyped, recursion_level == 1 ? mode.atTopLevel() : mode );
 			--recursion_level;
@@ -1054,5 +1055,5 @@
 
 			// promote candidate.cvtCost to .cost
-			promoteCvtCost( winners );
+			// promoteCvtCost( winners );
 
 			// produce ambiguous errors, if applicable
Index: src/ResolvExpr/SatisfyAssertions.cpp
===================================================================
--- src/ResolvExpr/SatisfyAssertions.cpp	(revision 0b0a285cb86eb63d47d96868ae2d0f99f753448c)
+++ src/ResolvExpr/SatisfyAssertions.cpp	(revision c84dd616ea09c9d1bd0ef0c0fba904ff70c4d83d)
@@ -16,4 +16,5 @@
 #include "SatisfyAssertions.hpp"
 
+#include <iostream>
 #include <algorithm>
 #include <cassert>
@@ -45,4 +46,6 @@
 #include "SymTab/Mangler.h"
 
+
+
 namespace ResolvExpr {
 
@@ -65,5 +68,9 @@
 			ast::AssertionSet && h, ast::AssertionSet && n, ast::OpenVarSet && o, ast::UniqueId rs )
 		: cdata( c ), adjType( at ), env( std::move( e ) ), have( std::move( h ) ),
-		  need( std::move( n ) ), open( std::move( o ) ), resnSlot( rs ) {}
+		  need( std::move( n ) ), open( std::move( o ) ), resnSlot( rs ) {
+			if (!have.empty()) {
+				// std::cerr << c.id->location << ':' << c.id->name << std::endl; // I think this was debugging code so I commented it
+			}
+		  }
 	};
 
@@ -139,10 +146,5 @@
 	};
 
-	/// Adds a captured assertion to the symbol table
-	void addToSymbolTable( const ast::AssertionSet & have, ast::SymbolTable & symtab ) {
-		for ( auto & i : have ) {
-			if ( i.second.isUsed ) { symtab.addId( i.first->var ); }
-		}
-	}
+	enum AssertionResult {Fail, Skip, Success} ;
 
 	/// Binds a single assertion, updating satisfaction state
@@ -155,5 +157,5 @@
 			"Assertion candidate does not have a unique ID: %s", toString( candidate ).c_str() );
 
-		ast::Expr * varExpr = match.cdata.combine( cand->expr->location, cand->cvtCost );
+		ast::Expr * varExpr = match.cdata.combine( cand->expr->location, cand->cost );
 		varExpr->result = match.adjType;
 		if ( match.resnSlot ) { varExpr->inferred.resnSlots().emplace_back( match.resnSlot ); }
@@ -165,10 +167,13 @@
 
 	/// Satisfy a single assertion
-	bool satisfyAssertion( ast::AssertionList::value_type & assn, SatState & sat, bool allowConversion = false, bool skipUnbound = false) {
+	AssertionResult satisfyAssertion( ast::AssertionList::value_type & assn, SatState & sat, bool skipUnbound = false) {
 		// skip unused assertions
-		if ( ! assn.second.isUsed ) return true;
+		// static unsigned int cnt = 0; // I think this was debugging code so I commented it
+		if ( ! assn.second.isUsed ) return AssertionResult::Success;
+
+		// if (assn.first->var->name[1] == '|') std::cerr << ++cnt << std::endl; // I think this was debugging code so I commented it
 
 		// find candidates that unify with the desired type
-		AssnCandidateList matches;
+		AssnCandidateList matches, inexactMatches;
 
 		std::vector<ast::SymbolTable::IdData> candidates;
@@ -179,10 +184,15 @@
 				.strict_as<ast::FunctionType>()->params[0]
 				.strict_as<ast::ReferenceType>()->base;
-			sat.cand->env.apply(thisArgType);
+			// sat.cand->env.apply(thisArgType);
+
+			if (auto inst = thisArgType.as<ast::TypeInstType>()) {
+				auto cls = sat.cand->env.lookup(*inst);
+				if (cls && cls->bound) thisArgType = cls->bound;
+			}
 
 			std::string otypeKey = "";
 			if (thisArgType.as<ast::PointerType>()) otypeKey = Mangle::Encoding::pointer;
 			else if (!isUnboundType(thisArgType)) otypeKey = Mangle::mangle(thisArgType, Mangle::Type | Mangle::NoGenericParams);
-			else if (skipUnbound) return false;
+			else if (skipUnbound) return AssertionResult::Skip;
 
 			candidates = sat.symtab.specialLookupId(kind, otypeKey);
@@ -212,7 +222,24 @@
 
 			ast::OpenVarSet closed;
-			findOpenVars( toType, newOpen, closed, newNeed, have, FirstClosed );
-			findOpenVars( adjType, newOpen, closed, newNeed, have, FirstOpen );
-			if ( allowConversion ) {
+			// findOpenVars( toType, newOpen, closed, newNeed, have, FirstClosed );
+			findOpenVars( adjType, newOpen, closed, newNeed, have, newEnv, FirstOpen );
+			ast::TypeEnvironment tempNewEnv {newEnv};
+
+			if ( unifyExact( toType, adjType, tempNewEnv, newNeed, have, newOpen, WidenMode {true, true} ) ) {
+				// set up binding slot for recursive assertions
+				ast::UniqueId crntResnSlot = 0;
+				if ( ! newNeed.empty() ) {
+					crntResnSlot = ++globalResnSlot;
+					for ( auto & a : newNeed ) { a.second.resnSlot = crntResnSlot; }
+				}
+
+				matches.emplace_back(
+					cdata, adjType, std::move( tempNewEnv ), std::move( have ), std::move( newNeed ),
+					std::move( newOpen ), crntResnSlot );
+			}
+			else if ( matches.empty() ) {
+				// restore invalidated env
+				// newEnv = sat.cand->env;
+				// newNeed.clear();
 				if ( auto c = commonType( toType, adjType, newEnv, newNeed, have, newOpen, WidenMode {true, true} ) ) {
 					// set up binding slot for recursive assertions
@@ -223,37 +250,24 @@
 					}
 
-					matches.emplace_back(
+					inexactMatches.emplace_back(
 						cdata, adjType, std::move( newEnv ), std::move( have ), std::move( newNeed ),
 						std::move( newOpen ), crntResnSlot );
 				}
 			}
-			else {
-				if ( unifyExact( toType, adjType, newEnv, newNeed, have, newOpen, WidenMode {true, true} ) ) {
-					// set up binding slot for recursive assertions
-					ast::UniqueId crntResnSlot = 0;
-					if ( ! newNeed.empty() ) {
-						crntResnSlot = ++globalResnSlot;
-						for ( auto & a : newNeed ) { a.second.resnSlot = crntResnSlot; }
-					}
-
-					matches.emplace_back(
-						cdata, adjType, std::move( newEnv ), std::move( have ), std::move( newNeed ),
-						std::move( newOpen ), crntResnSlot );
-				}
-			}
 		}
 
 		// break if no satisfying match
-		if ( matches.empty() ) return false;
+		if ( matches.empty() ) matches = std::move(inexactMatches);
+		if ( matches.empty() ) return AssertionResult::Fail;
 
 		// defer if too many satisfying matches
 		if ( matches.size() > 1 ) {
 			sat.deferred.emplace_back( assn.first, assn.second, std::move( matches ) );
-			return true;
+			return AssertionResult::Success;
 		}
 
 		// otherwise bind unique match in ongoing scope
 		AssnCandidate & match = matches.front();
-		addToSymbolTable( match.have, sat.symtab );
+		// addToSymbolTable( match.have, sat.symtab );
 		sat.newNeed.insert( match.need.begin(), match.need.end() );
 		sat.cand->env = std::move( match.env );
@@ -261,5 +275,5 @@
 
 		bindAssertion( assn.first, assn.second, sat.cand, match, sat.inferred );
-		return true;
+		return AssertionResult::Success;
 	}
 
@@ -438,5 +452,4 @@
 		// for each current mutually-compatible set of assertions
 		for ( SatState & sat : sats ) {
-			bool allowConversion = false;
 			// stop this branch if a better option is already found
 			auto it = thresholds.find( pruneKey( *sat.cand ) );
@@ -447,18 +460,10 @@
 			for (unsigned resetCount = 0; ; ++resetCount) {
 				ast::AssertionList next;
-				resetTyVarRenaming();
 				// make initial pass at matching assertions
 				for ( auto & assn : sat.need ) {
+					resetTyVarRenaming();
 					// fail early if any assertion is not satisfiable
-					if ( ! satisfyAssertion( assn, sat, allowConversion, !next.empty() ) ) {
-						next.emplace_back(assn);
-						// goto nextSat;
-					}
-				}
-				// success
-				if (next.empty()) break;
-				// fail if nothing resolves
-				else if (next.size() == sat.need.size()) {
-					if (allowConversion) {
+					auto result = satisfyAssertion( assn, sat, !next.empty() );
+					if ( result == AssertionResult::Fail ) {
 						Indenter tabs{ 3 };
 						std::ostringstream ss;
@@ -466,16 +471,17 @@
 						print( ss, *sat.cand, ++tabs );
 						ss << (tabs-1) << "Could not satisfy assertion:\n";
-						ast::print( ss, next[0].first, tabs );
+						ast::print( ss, assn.first, tabs );
 
 						errors.emplace_back( ss.str() );
 						goto nextSat;
 					}
-
-					else {
-						allowConversion = true;
-						continue;
-					}
-				}
-				allowConversion = false;
+					else if ( result == AssertionResult::Skip ) {
+						next.emplace_back(assn);
+						// goto nextSat;
+					}
+				}
+				// success
+				if (next.empty()) break;
+
 				sat.need = std::move(next);
 			}
@@ -531,5 +537,5 @@
 						sat.cand->expr, std::move( compat.env ), std::move( compat.open ),
 						ast::AssertionSet{} /* need moved into satisfaction state */,
-						sat.cand->cost, sat.cand->cvtCost );
+						sat.cand->cost );
 
 					ast::AssertionSet nextNewNeed{ sat.newNeed };
@@ -544,5 +550,5 @@
 					for ( DeferRef r : compat.assns ) {
 						AssnCandidate match = r.match;
-						addToSymbolTable( match.have, nextSymtab );
+						// addToSymbolTable( match.have, nextSymtab );
 						nextNewNeed.insert( match.need.begin(), match.need.end() );
 
Index: src/ResolvExpr/Unify.cc
===================================================================
--- src/ResolvExpr/Unify.cc	(revision 0b0a285cb86eb63d47d96868ae2d0f99f753448c)
+++ src/ResolvExpr/Unify.cc	(revision c84dd616ea09c9d1bd0ef0c0fba904ff70c4d83d)
@@ -160,6 +160,6 @@
 		env.apply( newSecond );
 
-		findOpenVars( newFirst, open, closed, need, have, FirstClosed );
-		findOpenVars( newSecond, open, closed, need, have, FirstOpen );
+		// findOpenVars( newFirst, open, closed, need, have, FirstClosed );
+		findOpenVars( newSecond, open, closed, need, have, newEnv, FirstOpen );
 
 		return unifyExact(newFirst, newSecond, newEnv, need, have, open, noWiden() );
@@ -964,5 +964,6 @@
 			// check that the other type is compatible and named the same
 			auto otherInst = dynamic_cast< const XInstType * >( other );
-			if (otherInst && inst->name == otherInst->name) this->result = otherInst;
+			if (otherInst && inst->name == otherInst->name) 
+				this->result = otherInst;
 			return otherInst;
 		}
@@ -1049,6 +1050,9 @@
 
 		void postvisit( const ast::TypeInstType * typeInst ) {
-			assert( open.find( *typeInst ) == open.end() );
-			handleRefType( typeInst, type2 );
+			// assert( open.find( *typeInst ) == open.end() );
+			auto otherInst = dynamic_cast< const ast::TypeInstType * >( type2 );
+			if (otherInst && typeInst->name == otherInst->name) 
+				this->result = otherInst;
+			// return otherInst;
 		}
 
@@ -1161,6 +1165,6 @@
 	) {
 		ast::OpenVarSet closed;
-		findOpenVars( type1, open, closed, need, have, FirstClosed );
-		findOpenVars( type2, open, closed, need, have, FirstOpen );
+		// findOpenVars( type1, open, closed, need, have, FirstClosed );
+		findOpenVars( type2, open, closed, need, have, env, FirstOpen );
 		return unifyInexact(
 			type1, type2, env, need, have, open, WidenMode{ true, true }, common );
@@ -1179,7 +1183,10 @@
 			entry1 = var1 ? open.find( *var1 ) : open.end(),
 			entry2 = var2 ? open.find( *var2 ) : open.end();
-		bool isopen1 = entry1 != open.end();
-		bool isopen2 = entry2 != open.end();
-
+		// bool isopen1 = entry1 != open.end();
+		// bool isopen2 = entry2 != open.end();
+		bool isopen1 = var1 && env.lookup(*var1);
+		bool isopen2 = var2 && env.lookup(*var2);
+
+		/*
 		if ( isopen1 && isopen2 ) {
 			if ( entry1->second.kind != entry2->second.kind ) return false;
@@ -1190,9 +1197,20 @@
 			return env.bindVar( var1, type2, entry1->second, need, have, open, widen );
 		} else if ( isopen2 ) {
-			return env.bindVar( var2, type1, entry2->second, need, have, open, widen );
-		} else {
+			return env.bindVar( var2, type1, entry2->second, need, have, open, widen, symtab );
+		} */
+		if ( isopen1 && isopen2 ) {
+			if ( var1->base->kind != var2->base->kind ) return false;
+			return env.bindVarToVar(
+				var1, var2, ast::TypeData{ var1->base->kind, var1->base->sized||var2->base->sized }, need, have,
+				open, widen );
+		} else if ( isopen1 ) {
+			return env.bindVar( var1, type2, ast::TypeData{var1->base}, need, have, open, widen );
+		} else if ( isopen2 ) {
+			return env.bindVar( var2, type1, ast::TypeData{var2->base}, need, have, open, widen );
+		}else {
 			return ast::Pass<Unify_new>::read(
 				type1, type2, env, need, have, open, widen );
 		}
+		
 	}
 
Index: src/SynTree/Expression.cc
===================================================================
--- src/SynTree/Expression.cc	(revision 0b0a285cb86eb63d47d96868ae2d0f99f753448c)
+++ src/SynTree/Expression.cc	(revision c84dd616ea09c9d1bd0ef0c0fba904ff70c4d83d)
@@ -267,13 +267,13 @@
 }
 
-CastExpr::CastExpr( Expression * arg, Type * toType, bool isGenerated ) : arg(arg), isGenerated( isGenerated ) {
+CastExpr::CastExpr( Expression * arg, Type * toType, bool isGenerated, CastKind kind ) : arg(arg), isGenerated( isGenerated ), kind( kind ) {
 	set_result(toType);
 }
 
-CastExpr::CastExpr( Expression * arg, bool isGenerated ) : arg(arg), isGenerated( isGenerated ) {
+CastExpr::CastExpr( Expression * arg, bool isGenerated, CastKind kind ) : arg(arg), isGenerated( isGenerated ), kind( kind ) {
 	set_result( new VoidType( Type::Qualifiers() ) );
 }
 
-CastExpr::CastExpr( const CastExpr & other ) : Expression( other ), arg( maybeClone( other.arg ) ), isGenerated( other.isGenerated ) {
+CastExpr::CastExpr( const CastExpr & other ) : Expression( other ), arg( maybeClone( other.arg ) ), isGenerated( other.isGenerated ), kind( other.kind ) {
 }
 
Index: src/SynTree/Expression.h
===================================================================
--- src/SynTree/Expression.h	(revision 0b0a285cb86eb63d47d96868ae2d0f99f753448c)
+++ src/SynTree/Expression.h	(revision c84dd616ea09c9d1bd0ef0c0fba904ff70c4d83d)
@@ -271,6 +271,14 @@
 	bool isGenerated = true;
 
-	CastExpr( Expression * arg, bool isGenerated = true );
-	CastExpr( Expression * arg, Type * toType, bool isGenerated = true );
+	enum CastKind {
+		Default, // C
+		Coerce, // reinterpret cast
+		Return  // overload selection
+	};
+
+	CastKind kind = Default;
+
+	CastExpr( Expression * arg, bool isGenerated = true, CastKind kind = Default );
+	CastExpr( Expression * arg, Type * toType, bool isGenerated = true, CastKind kind = Default );
 	CastExpr( Expression * arg, void * ) = delete; // prevent accidentally passing pointers for isGenerated in the first constructor
 	CastExpr( const CastExpr & other );
Index: src/Tuples/TupleAssignment.cc
===================================================================
--- src/Tuples/TupleAssignment.cc	(revision 0b0a285cb86eb63d47d96868ae2d0f99f753448c)
+++ src/Tuples/TupleAssignment.cc	(revision c84dd616ea09c9d1bd0ef0c0fba904ff70c4d83d)
@@ -679,4 +679,5 @@
 
 				ResolvExpr::CandidateFinder finder( crntFinder.context, matcher->env );
+				finder.allowVoid = true;
 
 				try {
Index: src/Validate/Autogen.cpp
===================================================================
--- src/Validate/Autogen.cpp	(revision 0b0a285cb86eb63d47d96868ae2d0f99f753448c)
+++ src/Validate/Autogen.cpp	(revision c84dd616ea09c9d1bd0ef0c0fba904ff70c4d83d)
@@ -321,4 +321,5 @@
 void FuncGenerator::produceDecl( const ast::FunctionDecl * decl ) {
 	assert( nullptr != decl->stmts );
+	assert( decl->type_params.size() == getGenericParams( type ).size() );
 
 	definitions.push_back( decl );
@@ -356,5 +357,5 @@
 		decl->init = nullptr;
 		splice( assertions, decl->assertions );
-		oldToNew.emplace( std::make_pair( old_param, decl ) );
+		oldToNew.emplace( old_param, decl );
 		type_params.push_back( decl );
 	}
@@ -522,5 +523,5 @@
 	InitTweak::InitExpander_new srcParam( src );
 	// Assign to destination.
-	ast::Expr * dstSelect = new ast::MemberExpr(
+	ast::MemberExpr * dstSelect = new ast::MemberExpr(
 		location,
 		field,
@@ -574,5 +575,5 @@
 		}
 
-		ast::Expr * srcSelect = (srcParam) ? new ast::MemberExpr(
+		ast::MemberExpr * srcSelect = (srcParam) ? new ast::MemberExpr(
 			location, field, new ast::VariableExpr( location, srcParam )
 		) : nullptr;
Index: src/Validate/GenericParameter.cpp
===================================================================
--- src/Validate/GenericParameter.cpp	(revision 0b0a285cb86eb63d47d96868ae2d0f99f753448c)
+++ src/Validate/GenericParameter.cpp	(revision c84dd616ea09c9d1bd0ef0c0fba904ff70c4d83d)
@@ -120,5 +120,14 @@
 }
 
-struct ValidateGenericParamsCore : public ast::WithCodeLocation {
+bool isSizedPolymorphic( const ast::AggregateDecl * decl ) {
+	for ( const auto & param : decl->params ) {
+		if ( param->sized ) return true;
+	}
+	return false;
+}
+
+struct ValidateGenericParamsCore :
+		public ast::WithCodeLocation, public ast::WithGuards {
+	// Generic parameter filling and checks:
 	const ast::StructInstType * previsit( const ast::StructInstType * type ) {
 		assert( location );
@@ -129,4 +138,25 @@
 		assert( location );
 		return validateGeneric( *location, type );
+	}
+
+	// Check parameter and bitfield combinations:
+	bool insideSized = false;
+	void previsit( const ast::StructDecl * decl ) {
+		if ( isSizedPolymorphic( decl ) && !insideSized ) {
+			GuardValue( insideSized ) = true;
+		}
+	}
+
+	void previsit( const ast::UnionDecl * decl ) {
+		if ( isSizedPolymorphic( decl ) && !insideSized ) {
+			GuardValue( insideSized ) = true;
+		}
+	}
+
+	void previsit( const ast::ObjectDecl * decl ) {
+		if ( insideSized && decl->bitfieldWidth ) {
+			SemanticError( decl->location, decl,
+				"Cannot have bitfields inside a sized polymorphic structure." );
+		}
 	}
 };
Index: src/Validate/LinkReferenceToTypes.cpp
===================================================================
--- src/Validate/LinkReferenceToTypes.cpp	(revision 0b0a285cb86eb63d47d96868ae2d0f99f753448c)
+++ src/Validate/LinkReferenceToTypes.cpp	(revision c84dd616ea09c9d1bd0ef0c0fba904ff70c4d83d)
@@ -84,5 +84,5 @@
 		// Just linking in the node.
 		auto mut = ast::mutate( type );
-		mut->base = const_cast<ast::EnumDecl *>( decl );
+		mut->base = decl;
 		type = mut;
 	}
@@ -101,5 +101,5 @@
 		// Just linking in the node.
 		auto mut = ast::mutate( type );
-		mut->base = const_cast<ast::StructDecl *>( decl );
+		mut->base = decl;
 		type = mut;
 	}
@@ -118,5 +118,5 @@
 		// Just linking in the node.
 		auto mut = ast::mutate( type );
-		mut->base = const_cast<ast::UnionDecl *>( decl );
+		mut->base = decl;
 		type = mut;
 	}
@@ -141,5 +141,5 @@
 
 	// Just linking in the node.
-	mut->base = const_cast<ast::TraitDecl *>( decl );
+	mut->base = decl;
 
 	// Need to carry over the 'sized' status of each decl in the instance.
@@ -203,6 +203,4 @@
 	}
 
-	// The following section 
-
 	ForwardEnumsType::iterator fwds = forwardEnums.find( decl->name );
 	if ( fwds != forwardEnums.end() ) {
Index: src/Virtual/VirtualDtor.cpp
===================================================================
--- src/Virtual/VirtualDtor.cpp	(revision 0b0a285cb86eb63d47d96868ae2d0f99f753448c)
+++ src/Virtual/VirtualDtor.cpp	(revision c84dd616ea09c9d1bd0ef0c0fba904ff70c4d83d)
@@ -146,16 +146,18 @@
 
             CompoundStmt * dtorBody = mutate( decl->stmts.get() );
-            // Adds the following to the end of any actor/message dtor:
+            // Adds the following to the start of any actor/message dtor:
             //  __CFA_dtor_shutdown( this );
-            dtorBody->push_front( new ExprStmt(
-                decl->location,
-				new UntypedExpr (
-                    decl->location,
-					new NameExpr( decl->location, "__CFA_dtor_shutdown" ),
-					{
-                        new NameExpr( decl->location, decl->params.at(0)->name )
-					}
-				)
-			));
+            dtorBody->push_front( 
+                new IfStmt( decl->location,
+                    new UntypedExpr (
+                        decl->location,
+                        new NameExpr( decl->location, "__CFA_dtor_shutdown" ),
+                        {
+                            new NameExpr( decl->location, decl->params.at(0)->name )
+                        }
+                    ),
+                    new ReturnStmt( decl->location, nullptr )
+                )
+            );
             return;
         }
Index: src/main.cc
===================================================================
--- src/main.cc	(revision 0b0a285cb86eb63d47d96868ae2d0f99f753448c)
+++ src/main.cc	(revision c84dd616ea09c9d1bd0ef0c0fba904ff70c4d83d)
@@ -28,6 +28,4 @@
 #include <list>                             // for list
 #include <string>                           // for char_traits, operator<<
-
-using namespace std;
 
 #include "AST/Convert.hpp"
@@ -88,4 +86,6 @@
 #include "Virtual/VirtualDtor.hpp"           // for implementVirtDtors
 
+using namespace std;
+
 static void NewPass( const char * const name ) {
 	Stats::Heap::newPass( name );
@@ -335,6 +335,6 @@
 
 		PASS( "Fix Qualified Types", Validate::fixQualifiedTypes, transUnit );
+		PASS( "Eliminate Typedef", Validate::eliminateTypedef, transUnit );
 		PASS( "Hoist Struct", Validate::hoistStruct, transUnit );
-		PASS( "Eliminate Typedef", Validate::eliminateTypedef, transUnit );
 		PASS( "Validate Generic Parameters", Validate::fillGenericParameters, transUnit );
 		PASS( "Translate Dimensions", Validate::translateDimensionParameters, transUnit );
