Index: src/GenPoly/Lvalue.cc
===================================================================
--- src/GenPoly/Lvalue.cc	(revision b1ccdfd6bc35f49ab6963b6540bd8e5bbab5efed)
+++ src/GenPoly/Lvalue.cc	(revision 31cb252a8222a2548a51e31ed5b88c50fc4d4752)
@@ -59,5 +59,5 @@
 		}
 
-		struct ReferenceConversions final {
+		struct ReferenceConversions final : public WithStmtsToAdd {
 			Expression * postmutate( CastExpr * castExpr );
 			Expression * postmutate( AddressExpr * addrExpr );
@@ -279,125 +279,242 @@
 			// pointer casts in the right places.
 
-			// conversion to reference type
-			if ( ReferenceType * refType = dynamic_cast< ReferenceType * >( castExpr->result ) ) {
-				(void)refType;
-				if ( ReferenceType * otherRef = dynamic_cast< ReferenceType * >( castExpr->arg->result ) ) {
-					// nothing to do if casting from reference to reference.
-					(void)otherRef;
-					PRINT( std::cerr << "convert reference to reference -- nop" << std::endl; )
-					if ( isIntrinsicReference( castExpr->arg ) ) {
-						Expression * callExpr = castExpr->arg;
-						PRINT(
-							std::cerr << "but arg is deref -- &" << std::endl;
-							std::cerr << callExpr << std::endl;
-						)
-						callExpr = new AddressExpr( callExpr ); // this doesn't work properly for multiple casts
-						delete callExpr->result;
-						callExpr->set_result( refType->clone() );
-						// move environment out to new top-level
-						callExpr->env = castExpr->env;
-						castExpr->arg = nullptr;
-						castExpr->env = nullptr;
-						delete castExpr;
-						return callExpr;
-					}
-					int depth1 = refType->referenceDepth();
-					int depth2 = otherRef->referenceDepth();
-					int diff = depth1-depth2;
-					if ( diff == 0 ) {
-						// conversion between references of the same depth
-						assertf( depth1 == depth2, "non-intrinsic reference with cast of reference to reference not yet supported: %d %d %s", depth1, depth2, toString( castExpr ).c_str() );
-						PRINT( std::cerr << castExpr << std::endl; )
-						return castExpr;
-					} else if ( diff < 0 ) {
-						// conversion from reference to reference with less depth (e.g. int && -> int &): add dereferences
-						Expression * ret = castExpr->arg;
-						for ( int i = 0; i < diff; ++i ) {
-							ret = mkDeref( ret );
-						}
-						ret->env = castExpr->env;
-						delete ret->result;
-						ret->result = castExpr->result;
-						ret->result->set_lvalue( true ); // ensure result is lvalue
-						castExpr->env = nullptr;
-						castExpr->arg = nullptr;
-						castExpr->result = nullptr;
-						delete castExpr;
-						return ret;
-					} else if ( diff > 0 ) {
-						// conversion from reference to reference with more depth (e.g. int & -> int &&): add address-of
-						Expression * ret = castExpr->arg;
-						for ( int i = 0; i < diff; ++i ) {
-							ret = new AddressExpr( ret );
-						}
-						ret->env = castExpr->env;
-						delete ret->result;
-						ret->result = castExpr->result;
-						castExpr->env = nullptr;
-						castExpr->arg = nullptr;
-						castExpr->result = nullptr;
-						delete castExpr;
-						return ret;
-					}
-
-					assertf( depth1 == depth2, "non-intrinsic reference with cast of reference to reference not yet supported: %d %d %s", depth1, depth2, toString( castExpr ).c_str() );
-					PRINT( std::cerr << castExpr << std::endl; )
+			// need to reorganize this so that depth difference is the determining factor in what code is run, rather than whether something is reference type or not.
+
+			Type * destType = castExpr->result;
+			Type * srcType = castExpr->arg->result;
+			int depth1 = destType->referenceDepth();
+			int depth2 = srcType->referenceDepth();
+			int diff = depth1 - depth2;
+
+			if ( diff > 0 && ! srcType->get_lvalue() ) {
+				// rvalue to reference conversion -- introduce temporary
+				// know that reference depth of cast argument is 0, need to introduce n temporaries for reference depth of n, e.g.
+				//   (int &&&)3;
+				// becomes
+				//   int __ref_tmp_0 = 3;
+				//   int & __ref_tmp_1 = _&_ref_tmp_0;
+				//   int && __ref_tmp_2 = &__ref_tmp_1;
+				//   &__ref_tmp_2;
+
+				static UniqueName tempNamer( "__ref_tmp_" );
+				ObjectDecl * temp = ObjectDecl::newObject( tempNamer.newName(), castExpr->arg->result->clone(), new SingleInit( castExpr->arg ) );
+				PRINT( std::cerr << "made temp: " << temp << std::endl; )
+				stmtsToAddBefore.push_back( new DeclStmt( temp ) );
+				for ( int i = 0; i < depth1-1; i++ ) {
+					ObjectDecl * newTemp = ObjectDecl::newObject( tempNamer.newName(), new ReferenceType( Type::Qualifiers(), temp->type->clone() ), new SingleInit( new AddressExpr( new VariableExpr( temp ) ) ) );
+					PRINT( std::cerr << "made temp" << i << ": " << newTemp << std::endl; )
+					stmtsToAddBefore.push_back( new DeclStmt( newTemp ) );
+					temp = newTemp;
+				}
+				// update diff so that remaining code works out correctly
+				castExpr->arg = new VariableExpr( temp );
+				PRINT( std::cerr << "update cast to: " << castExpr << std::endl; )
+				srcType = castExpr->arg->result;
+				depth2 = srcType->referenceDepth();
+				diff = depth1 - depth2;
+				assert( diff == 1 );
+			}
+
+			PRINT (
+				if ( depth1 || depth2 ) {
+					std::cerr << "destType: " << destType << " / srcType: " << srcType << std::endl;
+					std::cerr << "depth: " << depth1 << " / " << depth2 << std::endl;
+				}
+			)
+			if ( diff > 0 ) {
+				// conversion to type with more depth (e.g. int & -> int &&): add address-of for each level of difference
+				Expression * ret = castExpr->arg;
+				for ( int i = 0; i < diff; ++i ) {
+					ret = new AddressExpr( ret );
+				}
+				if ( srcType->get_lvalue() && srcType->get_qualifiers() != strict_dynamic_cast<ReferenceType *>( destType )->base->get_qualifiers() ) {
+					// must keep cast if cast-to type is different from the actual type
+					castExpr->arg = ret;
 					return castExpr;
-				} else if ( castExpr->arg->result->get_lvalue() ) {
-					// conversion from lvalue to reference
-					// xxx - keep cast, but turn into pointer cast??
-					// xxx - memory
-					PRINT(
-						std::cerr << "convert lvalue to reference -- &" << std::endl;
-						std::cerr << castExpr->arg << std::endl;
-					)
-					AddressExpr * ret = new AddressExpr( castExpr->arg );
-					if ( refType->base->get_qualifiers() != castExpr->arg->result->get_qualifiers() ) {
-						// must keep cast if cast-to type is different from the actual type
-						castExpr->arg = ret;
-						return castExpr;
-					}
-					ret->env = castExpr->env;
-					delete ret->result;
-					ret->result = castExpr->result;
-					castExpr->env = nullptr;
-					castExpr->arg = nullptr;
-					castExpr->result = nullptr;
-					delete castExpr;
-					return ret;
-				} else {
-					// rvalue to reference conversion -- introduce temporary
-				}
-				assertf( false, "Only conversions to reference from lvalue are currently supported: %s", toString( castExpr ).c_str() );
-			} else if ( ReferenceType * refType = dynamic_cast< ReferenceType * >( castExpr->arg->result ) ) {
-				(void)refType;
-				// conversion from reference to rvalue
-				PRINT(
-					std::cerr << "convert reference to rvalue -- *" << std::endl;
-					std::cerr << "was = " << castExpr << std::endl;
-				)
+				}
+				ret->env = castExpr->env;
+				delete ret->result;
+				ret->result = castExpr->result;
+				castExpr->env = nullptr;
+				castExpr->arg = nullptr;
+				castExpr->result = nullptr;
+				delete castExpr;
+				return ret;
+			} else if ( diff < 0 ) {
+				// conversion to type with less depth (e.g. int && -> int &): add dereferences for each level of difference
+				diff = -diff; // care only about magnitude now
 				Expression * ret = castExpr->arg;
-				TypeSubstitution * env = castExpr->env;
-				castExpr->set_env( nullptr );
-				if ( ! isIntrinsicReference( ret ) ) {
-					// dereference if not already dereferenced
+				for ( int i = 0; i < diff; ++i ) {
 					ret = mkDeref( ret );
 				}
-				if ( ResolvExpr::typesCompatibleIgnoreQualifiers( castExpr->result, castExpr->arg->result->stripReferences(), SymTab::Indexer() ) ) {
-					// can remove cast if types are compatible, changing expression type to value type
-					ret->result = castExpr->result->clone();
-					ret->result->set_lvalue( true );  // ensure result is lvalue
-					castExpr->arg = nullptr;
-					delete castExpr;
-				} else {
+				if ( ! ResolvExpr::typesCompatibleIgnoreQualifiers( destType->stripReferences(), srcType->stripReferences(), SymTab::Indexer() ) ) {
 					// must keep cast if types are different
 					castExpr->arg = ret;
-					ret = castExpr;
-				}
-				ret->set_env( env );
-				PRINT( std::cerr << "now: " << ret << std::endl; )
+					return castExpr;
+				}
+				ret->env = castExpr->env;
+				delete ret->result;
+				ret->result = castExpr->result;
+				ret->result->set_lvalue( true ); // ensure result is lvalue
+				castExpr->env = nullptr;
+				castExpr->arg = nullptr;
+				castExpr->result = nullptr;
+				delete castExpr;
 				return ret;
-			}
-			return castExpr;
+			} else {
+				assert( diff == 0 );
+				// conversion between references of the same depth
+				return castExpr;
+			}
+
+			// // conversion to reference type
+			// if ( ReferenceType * refType = dynamic_cast< ReferenceType * >( castExpr->result ) ) {
+			// 	(void)refType;
+			// 	if ( ReferenceType * otherRef = dynamic_cast< ReferenceType * >( castExpr->arg->result ) ) {
+			// 		// nothing to do if casting from reference to reference.
+			// 		(void)otherRef;
+			// 		PRINT( std::cerr << "convert reference to reference -- nop" << std::endl; )
+			// 		if ( isIntrinsicReference( castExpr->arg ) ) {
+			// 			Expression * callExpr = castExpr->arg;
+			// 			PRINT(
+			// 				std::cerr << "but arg is deref -- &" << std::endl;
+			// 				std::cerr << callExpr << std::endl;
+			// 			)
+			// 			callExpr = new AddressExpr( callExpr ); // this doesn't work properly for multiple casts
+			// 			delete callExpr->result;
+			// 			callExpr->set_result( refType->clone() );
+			// 			// move environment out to new top-level
+			// 			callExpr->env = castExpr->env;
+			// 			castExpr->arg = nullptr;
+			// 			castExpr->env = nullptr;
+			// 			delete castExpr;
+			// 			return callExpr;
+			// 		}
+			// 		int depth1 = refType->referenceDepth();
+			// 		int depth2 = otherRef->referenceDepth();
+			// 		int diff = depth1-depth2;
+			// 		if ( diff == 0 ) {
+			// 			// conversion between references of the same depth
+			// 			assertf( depth1 == depth2, "non-intrinsic reference with cast of reference to reference not yet supported: %d %d %s", depth1, depth2, toString( castExpr ).c_str() );
+			// 			PRINT( std::cerr << castExpr << std::endl; )
+			// 			return castExpr;
+			// 		} else if ( diff < 0 ) {
+			// 			// conversion from reference to reference with less depth (e.g. int && -> int &): add dereferences
+			// 			Expression * ret = castExpr->arg;
+			// 			for ( int i = 0; i < diff; ++i ) {
+			// 				ret = mkDeref( ret );
+			// 			}
+			// 			ret->env = castExpr->env;
+			// 			delete ret->result;
+			// 			ret->result = castExpr->result;
+			// 			ret->result->set_lvalue( true ); // ensure result is lvalue
+			// 			castExpr->env = nullptr;
+			// 			castExpr->arg = nullptr;
+			// 			castExpr->result = nullptr;
+			// 			delete castExpr;
+			// 			return ret;
+			// 		} else if ( diff > 0 ) {
+			// 			// conversion from reference to reference with more depth (e.g. int & -> int &&): add address-of
+			// 			Expression * ret = castExpr->arg;
+			// 			for ( int i = 0; i < diff; ++i ) {
+			// 				ret = new AddressExpr( ret );
+			// 			}
+			// 			ret->env = castExpr->env;
+			// 			delete ret->result;
+			// 			ret->result = castExpr->result;
+			// 			castExpr->env = nullptr;
+			// 			castExpr->arg = nullptr;
+			// 			castExpr->result = nullptr;
+			// 			delete castExpr;
+			// 			return ret;
+			// 		}
+
+			// 		assertf( depth1 == depth2, "non-intrinsic reference with cast of reference to reference not yet supported: %d %d %s", depth1, depth2, toString( castExpr ).c_str() );
+			// 		PRINT( std::cerr << castExpr << std::endl; )
+			// 		return castExpr;
+			// 	} else if ( castExpr->arg->result->get_lvalue() ) {
+			// 		// conversion from lvalue to reference
+			// 		// xxx - keep cast, but turn into pointer cast??
+			// 		// xxx - memory
+			// 		PRINT(
+			// 			std::cerr << "convert lvalue to reference -- &" << std::endl;
+			// 			std::cerr << castExpr->arg << std::endl;
+			// 		)
+			// 		AddressExpr * ret = new AddressExpr( castExpr->arg );
+			// 		if ( refType->base->get_qualifiers() != castExpr->arg->result->get_qualifiers() ) {
+			// 			// must keep cast if cast-to type is different from the actual type
+			// 			castExpr->arg = ret;
+			// 			return castExpr;
+			// 		}
+			// 		ret->env = castExpr->env;
+			// 		delete ret->result;
+			// 		ret->result = castExpr->result;
+			// 		castExpr->env = nullptr;
+			// 		castExpr->arg = nullptr;
+			// 		castExpr->result = nullptr;
+			// 		delete castExpr;
+			// 		return ret;
+			// 	} else {
+			// 		// rvalue to reference conversion -- introduce temporary
+			// 		// know that reference depth of cast argument is 0, need to introduce n temporaries for reference depth of n, e.g.
+			// 		//   (int &&&)3;
+			// 		// becomes
+			// 		//   int __ref_tmp_0 = 3;
+			// 		//   int & __ref_tmp_1 = _&_ref_tmp_0;
+			// 		//   int && __ref_tmp_2 = &__ref_tmp_1;
+			// 		//   &__ref_tmp_2;
+
+			// 		static UniqueName tempNamer( "__ref_tmp_" );
+			// 		ObjectDecl * temp = ObjectDecl::newObject( tempNamer.newName(), castExpr->arg->result->clone(), new SingleInit( castExpr->arg ) );
+			// 		stmtsToAddBefore.push_back( new DeclStmt( temp ) );
+			// 		auto depth = castExpr->result->referenceDepth();
+			// 		for ( int i = 0; i < depth-1; i++ ) {
+			// 			ObjectDecl * newTemp = ObjectDecl::newObject( tempNamer.newName(), new ReferenceType( Type::Qualifiers(), temp->type->clone() ), new SingleInit( new AddressExpr( new VariableExpr( temp ) ) ) );
+			// 			stmtsToAddBefore.push_back( new DeclStmt( newTemp ) );
+			// 			temp = newTemp;
+			// 		}
+			// 		Expression * ret = new AddressExpr( new VariableExpr( temp ) );
+			// 		// for ( int i = 0; i < depth; ++i ) {
+			// 		// 	ret = mkDeref( ret );
+			// 		// }
+			// 		ret->result = castExpr->result;
+			// 		ret->result->set_lvalue( true ); // ensure result is lvalue
+			// 		ret->env = castExpr->env;
+			// 		castExpr->arg = nullptr;
+			// 		castExpr->env = nullptr;
+			// 		castExpr->result = nullptr;
+			// 		delete castExpr;
+			// 		return ret;
+			// 	}
+			// } else if ( ReferenceType * refType = dynamic_cast< ReferenceType * >( castExpr->arg->result ) ) {
+			// 	(void)refType;
+			// 	// conversion from reference to rvalue
+			// 	PRINT(
+			// 		std::cerr << "convert reference to rvalue -- *" << std::endl;
+			// 		std::cerr << "was = " << castExpr << std::endl;
+			// 	)
+			// 	Expression * ret = castExpr->arg;
+			// 	TypeSubstitution * env = castExpr->env;
+			// 	castExpr->set_env( nullptr );
+			// 	if ( ! isIntrinsicReference( ret ) ) {
+			// 		// dereference if not already dereferenced
+			// 		ret = mkDeref( ret );
+			// 	}
+			// 	if ( ResolvExpr::typesCompatibleIgnoreQualifiers( castExpr->result, castExpr->arg->result->stripReferences(), SymTab::Indexer() ) ) {
+			// 		// can remove cast if types are compatible, changing expression type to value type
+			// 		ret->result = castExpr->result->clone();
+			// 		ret->result->set_lvalue( true );  // ensure result is lvalue
+			// 		castExpr->arg = nullptr;
+			// 		delete castExpr;
+			// 	} else {
+			// 		// must keep cast if types are different
+			// 		castExpr->arg = ret;
+			// 		ret = castExpr;
+			// 	}
+			// 	ret->set_env( env );
+			// 	PRINT( std::cerr << "now: " << ret << std::endl; )
+			// 	return ret;
+			// }
+			// return castExpr;
 		}
 
