Index: src/ResolvExpr/AlternativeFinder.cc
===================================================================
--- src/ResolvExpr/AlternativeFinder.cc	(revision a1a17a744695781ffbd73827d771f9063f9c11ba)
+++ src/ResolvExpr/AlternativeFinder.cc	(revision 59c034c60dc140fa869761785dbeccbd2624f249)
@@ -698,9 +698,10 @@
 			const ExplodedArgs& args, std::vector<ArgPack>& results, std::size_t& genStart,
 			const SymTab::Indexer& indexer, unsigned nTuples = 0 ) {
-		if ( TupleType* tupleType = dynamic_cast<TupleType*>( formalType ) ) {
+		if ( TupleType * tupleType = dynamic_cast<TupleType*>( formalType ) ) {
 			// formalType is a TupleType - group actuals into a TupleExpr
 			++nTuples;
 			for ( Type* type : *tupleType ) {
 				// xxx - dropping initializer changes behaviour from previous, but seems correct
+				// ^^^ need to handle the case where a tuple has a default argument
 				if ( ! instantiateArgument(
 						type, nullptr, args, results, genStart, indexer, nTuples ) )
@@ -713,5 +714,5 @@
 			}
 			return true;
-		} else if ( TypeInstType* ttype = Tuples::isTtype( formalType ) ) {
+		} else if ( TypeInstType * ttype = Tuples::isTtype( formalType ) ) {
 			// formalType is a ttype, consumes all remaining arguments
 			// xxx - mixing default arguments with variadic??
@@ -916,5 +917,5 @@
 				// consider only first exploded actual
 				Expression* expr = expl.exprs.front().get();
-				Type* actualType = expr->get_result()->clone();
+				Type* actualType = expr->result->clone();
 
 				PRINT(
@@ -947,5 +948,5 @@
 		ApplicationExpr *appExpr = new ApplicationExpr( func.expr->clone() );
 		// sum cost and accumulate actuals
-		std::list<Expression*>& args = appExpr->get_args();
+		std::list<Expression*>& args = appExpr->args;
 		Cost cost = func.cost;
 		const ArgPack* pack = &result;
@@ -974,9 +975,9 @@
 		// add all type variables as open variables now so that those not used in the parameter
 		// list are still considered open.
-		funcEnv.add( funcType->get_forall() );
-
-		if ( targetType && ! targetType->isVoid() && ! funcType->get_returnVals().empty() ) {
+		funcEnv.add( funcType->forall );
+
+		if ( targetType && ! targetType->isVoid() && ! funcType->returnVals.empty() ) {
 			// attempt to narrow based on expected target type
-			Type * returnType = funcType->get_returnVals().front()->get_type();
+			Type * returnType = funcType->returnVals.front()->get_type();
 			if ( ! unify( returnType, targetType, funcEnv, funcNeed, funcHave, funcOpenVars,
 					indexer ) ) {
@@ -991,8 +992,8 @@
 		std::size_t genStart = 0;
 
-		for ( DeclarationWithType* formal : funcType->get_parameters() ) {
+		for ( DeclarationWithType* formal : funcType->parameters ) {
 			ObjectDecl* obj = strict_dynamic_cast< ObjectDecl* >( formal );
 			if ( ! instantiateArgument(
-					obj->get_type(), obj->get_init(), args, results, genStart, indexer ) )
+					obj->type, obj->init, args, results, genStart, indexer ) )
 				return;
 		}
@@ -1075,5 +1076,5 @@
 	void AlternativeFinder::Finder::postvisit( UntypedExpr *untypedExpr ) {
 		AlternativeFinder funcFinder( indexer, env );
-		funcFinder.findWithAdjustment( untypedExpr->get_function() );
+		funcFinder.findWithAdjustment( untypedExpr->function );
 		// if there are no function alternatives, then proceeding is a waste of time.
 		if ( funcFinder.alternatives.empty() ) return;
@@ -1120,6 +1121,6 @@
 				)
 				// check if the type is pointer to function
-				if ( PointerType *pointer = dynamic_cast< PointerType* >( func->expr->get_result()->stripReferences() ) ) {
-					if ( FunctionType *function = dynamic_cast< FunctionType* >( pointer->get_base() ) ) {
+				if ( PointerType *pointer = dynamic_cast< PointerType* >( func->expr->result->stripReferences() ) ) {
+					if ( FunctionType *function = dynamic_cast< FunctionType* >( pointer->base ) ) {
 						Alternative newFunc( *func );
 						referenceToRvalueConversion( newFunc.expr, newFunc.cost );
@@ -1127,7 +1128,7 @@
 							std::back_inserter( candidates ) );
 					}
-				} else if ( TypeInstType *typeInst = dynamic_cast< TypeInstType* >( func->expr->get_result()->stripReferences() ) ) { // handle ftype (e.g. *? on function pointer)
+				} else if ( TypeInstType *typeInst = dynamic_cast< TypeInstType* >( func->expr->result->stripReferences() ) ) { // handle ftype (e.g. *? on function pointer)
 					EqvClass eqvClass;
-					if ( func->env.lookup( typeInst->get_name(), eqvClass ) && eqvClass.type ) {
+					if ( func->env.lookup( typeInst->name, eqvClass ) && eqvClass.type ) {
 						if ( FunctionType *function = dynamic_cast< FunctionType* >( eqvClass.type ) ) {
 							Alternative newFunc( *func );
@@ -1158,7 +1159,7 @@
 					// check if type is a pointer to function
 					if ( PointerType* pointer = dynamic_cast<PointerType*>(
-							funcOp->expr->get_result()->stripReferences() ) ) {
+							funcOp->expr->result->stripReferences() ) ) {
 						if ( FunctionType* function =
-								dynamic_cast<FunctionType*>( pointer->get_base() ) ) {
+								dynamic_cast<FunctionType*>( pointer->base ) ) {
 							Alternative newFunc( *funcOp );
 							referenceToRvalueConversion( newFunc.expr, newFunc.cost );
@@ -1182,11 +1183,11 @@
 			PRINT(
 				ApplicationExpr *appExpr = strict_dynamic_cast< ApplicationExpr* >( withFunc.expr );
-				PointerType *pointer = strict_dynamic_cast< PointerType* >( appExpr->get_function()->get_result() );
-				FunctionType *function = strict_dynamic_cast< FunctionType* >( pointer->get_base() );
-				std::cerr << "Case +++++++++++++ " << appExpr->get_function() << std::endl;
+				PointerType *pointer = strict_dynamic_cast< PointerType* >( appExpr->function->result );
+				FunctionType *function = strict_dynamic_cast< FunctionType* >( pointer->base );
+				std::cerr << "Case +++++++++++++ " << appExpr->function << std::endl;
 				std::cerr << "formals are:" << std::endl;
-				printAll( function->get_parameters(), std::cerr, 8 );
+				printAll( function->parameters, std::cerr, 8 );
 				std::cerr << "actuals are:" << std::endl;
-				printAll( appExpr->get_args(), std::cerr, 8 );
+				printAll( appExpr->args, std::cerr, 8 );
 				std::cerr << "bindings are:" << std::endl;
 				withFunc.env.print( std::cerr, 8 );
@@ -1229,5 +1230,5 @@
 	bool isLvalue( Expression *expr ) {
 		// xxx - recurse into tuples?
-		return expr->result && ( expr->get_result()->get_lvalue() || dynamic_cast< ReferenceType * >( expr->get_result() ) );
+		return expr->result && ( expr->result->get_lvalue() || dynamic_cast< ReferenceType * >( expr->result ) );
 	}
 
@@ -1291,4 +1292,6 @@
 			AssertionSet needAssertions, haveAssertions;
 			OpenVarSet openVars;
+
+			alt.env.extractOpenVars( openVars );
 
 			// It's possible that a cast can throw away some values in a multiply-valued expression.  (An example is a
@@ -1709,5 +1712,5 @@
 			AlternativeFinder finder( indexer, env );
 			finder.targetType = toType;
-			finder.findWithAdjustment( initExpr->get_expr() );
+			finder.findWithAdjustment( initExpr->expr );
 			for ( Alternative & alt : finder.get_alternatives() ) {
 				TypeEnvironment newEnv( alt.env );
@@ -1716,10 +1719,10 @@
 				PRINT(
 					std::cerr << "  @ " << toType << " " << initAlt.designation << std::endl;
-				 )
+				)
 				// It's possible that a cast can throw away some values in a multiply-valued expression.  (An example is a
 				// cast-to-void, which casts from one value to zero.)  Figure out the prefix of the subexpression results
 				// that are cast directly.  The candidate is invalid if it has fewer results than there are types to cast
 				// to.
-				int discardedValues = alt.expr->get_result()->size() - toType->size();
+				int discardedValues = alt.expr->result->size() - toType->size();
 				if ( discardedValues < 0 ) continue;
 				// xxx - may need to go into tuple types and extract relevant types and use unifyList. Note that currently, this does not
@@ -1728,5 +1731,5 @@
 				unify( toType, alt.expr->result, newEnv, needAssertions, haveAssertions, openVars, indexer ); // xxx - do some inspecting on this line... why isn't result bound to initAlt.type??
 
-				Cost thisCost = castCost( alt.expr->get_result(), toType, indexer, newEnv );
+				Cost thisCost = castCost( alt.expr->result, toType, indexer, newEnv );
 				if ( thisCost != Cost::infinity ) {
 					// count one safe conversion for each value that is thrown away
Index: src/Tuples/TupleAssignment.cc
===================================================================
--- src/Tuples/TupleAssignment.cc	(revision a1a17a744695781ffbd73827d771f9063f9c11ba)
+++ src/Tuples/TupleAssignment.cc	(revision 59c034c60dc140fa869761785dbeccbd2624f249)
@@ -231,5 +231,6 @@
 
 			ResolvExpr::AlternativeFinder finder{ currentFinder.get_indexer(),
-				currentFinder.get_environ() };
+				matcher->compositeEnv };
+
 			try {
 				finder.findWithAdjustment(*i);
@@ -349,4 +350,10 @@
 				ltmp.push_back( lobj );
 				rtmp.push_back( robj );
+
+				// resolve the cast expression so that rhsAlt return type is bound by the cast type as needed, and transfer the resulting environment
+				ResolvExpr::AlternativeFinder finder{ spotter.currentFinder.get_indexer(), compositeEnv };
+				finder.findWithAdjustment( rhsAlt.expr );
+				assert( finder.get_alternatives().size() == 1 );
+				compositeEnv = std::move( finder.get_alternatives().front().env );
 			}
 			tmpDecls.splice( tmpDecls.end(), ltmp );
