Index: src/ResolvExpr/AlternativeFinder.cc
===================================================================
--- src/ResolvExpr/AlternativeFinder.cc	(revision 62194cbf579b5084c2ebfafd9003eb7b9cb0fa7b)
+++ src/ResolvExpr/AlternativeFinder.cc	(revision 178e4ec86cc344917f0d9781c8bf4e520b7b2a53)
@@ -187,4 +187,7 @@
 		expr->accept( *this );
 		if ( failFast && alternatives.empty() ) {
+			PRINT(
+				std::cerr << "No reasonable alternatives for expression " << expr << std::endl;
+			)
 			throw SemanticError( "No reasonable alternatives for expression ", expr );
 		}
@@ -579,5 +582,5 @@
 	/// State to iteratively build a match of parameter expressions to arguments
 	struct ArgPack {
-		std::size_t parent;                ///< Index of parent pack 
+		std::size_t parent;                ///< Index of parent pack
 		std::unique_ptr<Expression> expr;  ///< The argument stored here
 		Cost cost;                         ///< The cost of this argument
@@ -590,28 +593,29 @@
 		unsigned nextExpl;                 ///< Index of next exploded element
 		unsigned explAlt;                  ///< Index of alternative for nextExpl > 0
-		
+
 		ArgPack()
 			: parent(0), expr(), cost(Cost::zero), env(), need(), have(), openVars(), nextArg(0),
+
 			  tupleStart(0), nextExpl(0), explAlt(0) {}
-		
-		ArgPack(const TypeEnvironment& env, const AssertionSet& need, const AssertionSet& have, 
+
+		ArgPack(const TypeEnvironment& env, const AssertionSet& need, const AssertionSet& have,
 				const OpenVarSet& openVars)
-			: parent(0), expr(), cost(Cost::zero), env(env), need(need), have(have), 
+			: parent(0), expr(), cost(Cost::zero), env(env), need(need), have(have),
 			  openVars(openVars), nextArg(0), tupleStart(0), nextExpl(0), explAlt(0) {}
-		
-		ArgPack(std::size_t parent, Expression* expr, TypeEnvironment&& env, AssertionSet&& need, 
-				AssertionSet&& have, OpenVarSet&& openVars, unsigned nextArg, 
-				unsigned tupleStart = 0, Cost cost = Cost::zero, unsigned nextExpl = 0, 
+
+		ArgPack(std::size_t parent, Expression* expr, TypeEnvironment&& env, AssertionSet&& need,
+				AssertionSet&& have, OpenVarSet&& openVars, unsigned nextArg,
+				unsigned tupleStart = 0, Cost cost = Cost::zero, unsigned nextExpl = 0,
 				unsigned explAlt = 0 )
-			: parent(parent), expr(expr->clone()), cost(cost), env(move(env)), need(move(need)), 
+			: parent(parent), expr(expr->clone()), cost(cost), env(move(env)), need(move(need)),
 			  have(move(have)), openVars(move(openVars)), nextArg(nextArg), tupleStart(tupleStart),
 			  nextExpl(nextExpl), explAlt(explAlt) {}
-		
-		ArgPack(const ArgPack& o, TypeEnvironment&& env, AssertionSet&& need, AssertionSet&& have, 
+
+		ArgPack(const ArgPack& o, TypeEnvironment&& env, AssertionSet&& need, AssertionSet&& have,
 				OpenVarSet&& openVars, unsigned nextArg, Cost added )
-			: parent(o.parent), expr(o.expr ? o.expr->clone() : nullptr), cost(o.cost + added), 
-			  env(move(env)), need(move(need)), have(move(have)), openVars(move(openVars)), 
+			: parent(o.parent), expr(o.expr ? o.expr->clone() : nullptr), cost(o.cost + added),
+			  env(move(env)), need(move(need)), have(move(have)), openVars(move(openVars)),
 			  nextArg(nextArg), tupleStart(o.tupleStart), nextExpl(0), explAlt(0) {}
-		
+
 		/// true iff this pack is in the middle of an exploded argument
 		bool hasExpl() const { return nextExpl > 0; }
@@ -621,5 +625,5 @@
 			return args[nextArg-1][explAlt];
 		}
-			  
+
 		/// Ends a tuple expression, consolidating the appropriate actuals
 		void endTuple( const std::vector<ArgPack>& packs ) {
@@ -641,6 +645,6 @@
 
 	/// Instantiates an argument to match a formal, returns false if no results left
-	bool instantiateArgument( Type* formalType, Initializer* initializer, 
-			const ExplodedArgs& args, std::vector<ArgPack>& results, std::size_t& genStart, 
+	bool instantiateArgument( Type* formalType, Initializer* initializer,
+			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 ) ) {
@@ -649,6 +653,6 @@
 			for ( Type* type : *tupleType ) {
 				// xxx - dropping initializer changes behaviour from previous, but seems correct
-				if ( ! instantiateArgument( 
-						type, nullptr, args, results, genStart, indexer, nTuples ) ) 
+				if ( ! instantiateArgument(
+						type, nullptr, args, results, genStart, indexer, nTuples ) )
 					return false;
 				nTuples = 0;
@@ -679,5 +683,5 @@
 					if ( results[i].hasExpl() ) {
 						const ExplodedActual& expl = results[i].getExpl( args );
-						
+
 						unsigned nextExpl = results[i].nextExpl + 1;
 						if ( nextExpl == expl.exprs.size() ) {
@@ -686,16 +690,16 @@
 
 						results.emplace_back(
-							i, expl.exprs[results[i].nextExpl].get(), copy(results[i].env), 
-							copy(results[i].need), copy(results[i].have), 
-							copy(results[i].openVars), nextArg, nTuples, Cost::zero, nextExpl, 
+							i, expl.exprs[results[i].nextExpl].get(), copy(results[i].env),
+							copy(results[i].need), copy(results[i].have),
+							copy(results[i].openVars), nextArg, nTuples, Cost::zero, nextExpl,
 							results[i].explAlt );
-						
+
 						continue;
 					}
-					
+
 					// finish result when out of arguments
 					if ( nextArg >= args.size() ) {
-						ArgPack newResult{ 
-							results[i].env, results[i].need, results[i].have, 
+						ArgPack newResult{
+							results[i].env, results[i].need, results[i].have,
 							results[i].openVars };
 						newResult.nextArg = nextArg;
@@ -717,11 +721,11 @@
 
 							if ( results[i].tupleStart > 0 && Tuples::isTtype( argType ) ) {
-								// the case where a ttype value is passed directly is special, 
+								// the case where a ttype value is passed directly is special,
 								// e.g. for argument forwarding purposes
-								// xxx - what if passing multiple arguments, last of which is 
+								// xxx - what if passing multiple arguments, last of which is
 								//       ttype?
-								// xxx - what would happen if unify was changed so that unifying 
-								//       tuple 
-								// types flattened both before unifying lists? then pass in 
+								// xxx - what would happen if unify was changed so that unifying
+								//       tuple
+								// types flattened both before unifying lists? then pass in
 								// TupleType (ttype) below.
 								--newResult.tupleStart;
@@ -734,9 +738,9 @@
 
 						// check unification for ttype before adding to final
-						if ( unify( ttype, argType, newResult.env, newResult.need, newResult.have, 
+						if ( unify( ttype, argType, newResult.env, newResult.need, newResult.have,
 								newResult.openVars, indexer ) ) {
 							finalResults.push_back( move(newResult) );
 						}
-						
+
 						continue;
 					}
@@ -745,5 +749,5 @@
 					for ( std::size_t j = 0; j < args[nextArg].size(); ++j ) {
 						const ExplodedActual& expl = args[nextArg][j];
-					
+
 						// fresh copies of parent parameters for this iteration
 						TypeEnvironment env = results[i].env;
@@ -755,7 +759,7 @@
 						if ( expl.exprs.empty() ) {
 							results.emplace_back(
-								results[i], move(env), copy(results[i].need), 
+								results[i], move(env), copy(results[i].need),
 								copy(results[i].have), move(openVars), nextArg + 1, expl.cost );
-							
+
 							continue;
 						}
@@ -763,6 +767,6 @@
 						// add new result
 						results.emplace_back(
-							i, expl.exprs.front().get(), move(env), copy(results[i].need), 
-							copy(results[i].have), move(openVars), nextArg + 1, 
+							i, expl.exprs.front().get(), move(env), copy(results[i].need),
+							copy(results[i].have), move(openVars), nextArg + 1,
 							nTuples, expl.cost, expl.exprs.size() == 1 ? 0 : 1, j );
 					}
@@ -790,5 +794,5 @@
 				const ExplodedActual& expl = results[i].getExpl( args );
 				Expression* expr = expl.exprs[results[i].nextExpl].get();
-				
+
 				TypeEnvironment env = results[i].env;
 				AssertionSet need = results[i].need, have = results[i].have;
@@ -804,5 +808,5 @@
 					std::cerr << std::endl;
 				)
-				
+
 				if ( unify( formalType, actualType, env, need, have, openVars, indexer ) ) {
 					unsigned nextExpl = results[i].nextExpl + 1;
@@ -810,7 +814,7 @@
 						nextExpl = 0;
 					}
-					
-					results.emplace_back( 
-						i, expr, move(env), move(need), move(have), move(openVars), nextArg, 
+
+					results.emplace_back(
+						i, expr, move(env), move(need), move(have), move(openVars), nextArg,
 						nTuples, Cost::zero, nextExpl, results[i].explAlt );
 				}
@@ -818,5 +822,5 @@
 				continue;
 			}
-			
+
 			// use default initializers if out of arguments
 			if ( nextArg >= args.size() ) {
@@ -827,8 +831,8 @@
 						OpenVarSet openVars = results[i].openVars;
 
-						if ( unify( formalType, cnst->get_type(), env, need, have, openVars, 
+						if ( unify( formalType, cnst->get_type(), env, need, have, openVars,
 								indexer ) ) {
 							results.emplace_back(
-								i, cnstExpr, move(env), move(need), move(have), 
+								i, cnstExpr, move(env), move(need), move(have),
 								move(openVars), nextArg, nTuples );
 						}
@@ -849,9 +853,9 @@
 
 				env.addActual( expl.env, openVars );
-				
+
 				// skip empty tuple arguments by (near-)cloning parent into next gen
 				if ( expl.exprs.empty() ) {
 					results.emplace_back(
-						results[i], move(env), move(need), move(have), move(openVars), 
+						results[i], move(env), move(need), move(have), move(openVars),
 						nextArg + 1, expl.cost );
 
@@ -875,5 +879,5 @@
 					// add new result
 					results.emplace_back(
-						i, expr, move(env), move(need), move(have), move(openVars), nextArg + 1, 
+						i, expr, move(env), move(need), move(have), move(openVars), nextArg + 1,
 						nTuples, expl.cost, expl.exprs.size() == 1 ? 0 : 1, j );
 				}
@@ -883,10 +887,10 @@
 		// reset for next parameter
 		genStart = genEnd;
-		
+
 		return genEnd != results.size();
 	}
 
 	template<typename OutputIterator>
-	void AlternativeFinder::validateFunctionAlternative( const Alternative &func, ArgPack& result, 
+	void AlternativeFinder::validateFunctionAlternative( const Alternative &func, ArgPack& result,
 			const std::vector<ArgPack>& results, OutputIterator out ) {
 		ApplicationExpr *appExpr = new ApplicationExpr( func.expr->clone() );
@@ -938,5 +942,5 @@
 		for ( DeclarationWithType* formal : funcType->get_parameters() ) {
 			ObjectDecl* obj = strict_dynamic_cast< ObjectDecl* >( formal );
-			if ( ! instantiateArgument( 
+			if ( ! instantiateArgument(
 					obj->get_type(), obj->get_init(), args, results, genStart, indexer ) )
 				return;
@@ -956,5 +960,5 @@
 					if ( results[i].hasExpl() ) {
 						const ExplodedActual& expl = results[i].getExpl( args );
-						
+
 						unsigned nextExpl = results[i].nextExpl + 1;
 						if ( nextExpl == expl.exprs.size() ) {
@@ -963,9 +967,9 @@
 
 						results.emplace_back(
-							i, expl.exprs[results[i].nextExpl].get(), copy(results[i].env), 
-							copy(results[i].need), copy(results[i].have), 
-							copy(results[i].openVars), nextArg, 0, Cost::zero, nextExpl, 
+							i, expl.exprs[results[i].nextExpl].get(), copy(results[i].env),
+							copy(results[i].need), copy(results[i].have),
+							copy(results[i].openVars), nextArg, 0, Cost::zero, nextExpl,
 							results[i].explAlt );
-						
+
 						continue;
 					}
@@ -990,8 +994,8 @@
 						// skip empty tuple arguments by (near-)cloning parent into next gen
 						if ( expl.exprs.empty() ) {
-							results.emplace_back( 
-								results[i], move(env), copy(results[i].need), 
+							results.emplace_back(
+								results[i], move(env), copy(results[i].need),
 								copy(results[i].have), move(openVars), nextArg + 1, expl.cost );
-							
+
 							continue;
 						}
@@ -999,6 +1003,6 @@
 						// add new result
 						results.emplace_back(
-							i, expl.exprs.front().get(), move(env), copy(results[i].need), 
-							copy(results[i].have), move(openVars), nextArg + 1, 0, 
+							i, expl.exprs.front().get(), move(env), copy(results[i].need),
+							copy(results[i].have), move(openVars), nextArg + 1, 0,
 							expl.cost, expl.exprs.size() == 1 ? 0 : 1, j );
 					}
@@ -1050,5 +1054,5 @@
 			auto& argE = argExpansions.back();
 			argE.reserve( arg.alternatives.size() );
-			
+
 			for ( const Alternative& actual : arg ) {
 				argE.emplace_back( actual, indexer );
@@ -1150,6 +1154,6 @@
 		findMinCost( candidates.begin(), candidates.end(), std::back_inserter( winners ) );
 
-		// function may return struct or union value, in which case we need to add alternatives 
-		// for implicitconversions to each of the anonymous members, must happen after findMinCost 
+		// function may return struct or union value, in which case we need to add alternatives
+		// for implicitconversions to each of the anonymous members, must happen after findMinCost
 		// since anon conversions are never the cheapest expression
 		for ( const Alternative & alt : winners ) {
@@ -1182,5 +1186,5 @@
 		for ( Alternative& alt : finder.alternatives ) {
 			if ( isLvalue( alt.expr ) ) {
-				alternatives.push_back( 
+				alternatives.push_back(
 					Alternative{ new AddressExpr( alt.expr->clone() ), alt.env, alt.cost } );
 			} // if
@@ -1231,5 +1235,5 @@
 
 		AltList candidates;
-		for ( Alternative& alt : finder.alternatives ) {
+		for ( Alternative & alt : finder.alternatives ) {
 			AssertionSet needAssertions, haveAssertions;
 			OpenVarSet openVars;
@@ -1244,14 +1248,22 @@
 			// allow casting a tuple to an atomic type (e.g. (int)([1, 2, 3]))
 			// unification run for side-effects
-			unify( castExpr->get_result(), alt.expr->get_result(), alt.env, needAssertions, 
+			unify( castExpr->get_result(), alt.expr->get_result(), alt.env, needAssertions,
 				haveAssertions, openVars, indexer );
-			Cost thisCost = castCost( alt.expr->get_result(), castExpr->get_result(), indexer, 
+			Cost thisCost = castCost( alt.expr->get_result(), castExpr->get_result(), indexer,
 				alt.env );
+			PRINT(
+				std::cerr << "working on cast with result: " << castExpr->result << std::endl;
+				std::cerr << "and expr type: " << alt.expr->result << std::endl;
+				std::cerr << "env: " << alt.env << std::endl;
+			)
 			if ( thisCost != Cost::infinity ) {
+				PRINT(
+					std::cerr << "has finite cost." << std::endl;
+				)
 				// count one safe conversion for each value that is thrown away
 				thisCost.incSafe( discardedValues );
-				Alternative newAlt( restructureCast( alt.expr->clone(), toType ), alt.env, 
+				Alternative newAlt( restructureCast( alt.expr->clone(), toType ), alt.env,
 					alt.cost, thisCost );
-				inferParameters( needAssertions, haveAssertions, newAlt, openVars, 
+				inferParameters( needAssertions, haveAssertions, newAlt, openVars,
 					back_inserter( candidates ) );
 			} // if
@@ -1542,8 +1554,8 @@
 	void AlternativeFinder::visit( UntypedTupleExpr *tupleExpr ) {
 		std::vector< AlternativeFinder > subExprAlternatives;
-		findSubExprs( tupleExpr->get_exprs().begin(), tupleExpr->get_exprs().end(), 
+		findSubExprs( tupleExpr->get_exprs().begin(), tupleExpr->get_exprs().end(),
 			back_inserter( subExprAlternatives ) );
 		std::vector< AltList > possibilities;
-		combos( subExprAlternatives.begin(), subExprAlternatives.end(), 
+		combos( subExprAlternatives.begin(), subExprAlternatives.end(),
 			back_inserter( possibilities ) );
 		for ( const AltList& alts : possibilities ) {
@@ -1553,5 +1565,5 @@
 			TypeEnvironment compositeEnv;
 			simpleCombineEnvironments( alts.begin(), alts.end(), compositeEnv );
-			alternatives.push_back( 
+			alternatives.push_back(
 				Alternative{ new TupleExpr( exprs ), compositeEnv, sumCost( alts ) } );
 		} // for
Index: src/ResolvExpr/AlternativeFinder.h
===================================================================
--- src/ResolvExpr/AlternativeFinder.h	(revision 62194cbf579b5084c2ebfafd9003eb7b9cb0fa7b)
+++ src/ResolvExpr/AlternativeFinder.h	(revision 178e4ec86cc344917f0d9781c8bf4e520b7b2a53)
@@ -32,10 +32,10 @@
 
 namespace ResolvExpr {
-	class ArgPack;
-	
-	/// First index is which argument, second index is which alternative for that argument, 
+	struct ArgPack;
+
+	/// First index is which argument, second index is which alternative for that argument,
 	/// third index is which exploded element of that alternative
 	using ExplodedArgs = std::vector< std::vector< ExplodedActual > >;
-	
+
 	class AlternativeFinder : public Visitor {
 	  public:
@@ -43,14 +43,14 @@
 
 		AlternativeFinder( const AlternativeFinder& o )
-			: indexer(o.indexer), alternatives(o.alternatives), env(o.env), 
+			: indexer(o.indexer), alternatives(o.alternatives), env(o.env),
 			  targetType(o.targetType) {}
-		
+
 		AlternativeFinder( AlternativeFinder&& o )
-			: indexer(o.indexer), alternatives(std::move(o.alternatives)), env(o.env), 
+			: indexer(o.indexer), alternatives(std::move(o.alternatives)), env(o.env),
 			  targetType(o.targetType) {}
-		
+
 		AlternativeFinder& operator= ( const AlternativeFinder& o ) {
 			if (&o == this) return *this;
-			
+
 			// horrific nasty hack to rebind references...
 			alternatives.~AltList();
@@ -61,5 +61,5 @@
 		AlternativeFinder& operator= ( AlternativeFinder&& o ) {
 			if (&o == this) return *this;
-			
+
 			// horrific nasty hack to rebind references...
 			alternatives.~AltList();
Index: src/ResolvExpr/PtrsAssignable.cc
===================================================================
--- src/ResolvExpr/PtrsAssignable.cc	(revision 62194cbf579b5084c2ebfafd9003eb7b9cb0fa7b)
+++ src/ResolvExpr/PtrsAssignable.cc	(revision 178e4ec86cc344917f0d9781c8bf4e520b7b2a53)
@@ -68,9 +68,6 @@
 
 	void PtrsAssignable::visit( __attribute((unused)) VoidType *voidType ) {
-		if ( ! dynamic_cast< FunctionType* >( dest ) ) {
-			// T * = void * is safe for any T that is not a function type.
-			// xxx - this should be unsafe...
-			result = 1;
-		} // if
+		// T * = void * is disallowed - this is a change from C, where any
+		// void * can be assigned or passed to a non-void pointer without a cast.
 	}
 
Index: src/ResolvExpr/TypeEnvironment.cc
===================================================================
--- src/ResolvExpr/TypeEnvironment.cc	(revision 62194cbf579b5084c2ebfafd9003eb7b9cb0fa7b)
+++ src/ResolvExpr/TypeEnvironment.cc	(revision 178e4ec86cc344917f0d9781c8bf4e520b7b2a53)
@@ -212,4 +212,8 @@
 	}
 
+	std::ostream & operator<<( std::ostream & out, const TypeEnvironment & env ) {
+		env.print( out );
+		return out;
+	}
 } // namespace ResolvExpr
 
Index: src/ResolvExpr/TypeEnvironment.h
===================================================================
--- src/ResolvExpr/TypeEnvironment.h	(revision 62194cbf579b5084c2ebfafd9003eb7b9cb0fa7b)
+++ src/ResolvExpr/TypeEnvironment.h	(revision 178e4ec86cc344917f0d9781c8bf4e520b7b2a53)
@@ -86,5 +86,5 @@
 		TypeEnvironment *clone() const { return new TypeEnvironment( *this ); }
 
-		/// Iteratively adds the environment of a new actual (with allowWidening = false), 
+		/// Iteratively adds the environment of a new actual (with allowWidening = false),
 		/// and extracts open variables.
 		void addActual( const TypeEnvironment& actualEnv, OpenVarSet& openVars );
@@ -114,4 +114,6 @@
 		return sub.applyFree( type );
 	}
+
+	std::ostream & operator<<( std::ostream & out, const TypeEnvironment & env );
 } // namespace ResolvExpr
 
