Index: src/GenPoly/Lvalue.cc
===================================================================
--- src/GenPoly/Lvalue.cc	(revision d3356273b5793b3e8f1af94e178f3f46c47bea72)
+++ src/GenPoly/Lvalue.cc	(revision 2afec66345a55139c8738d36411816676b2fe4d3)
@@ -66,4 +66,5 @@
 		struct ReferenceConversions final {
 			Expression * postmutate( CastExpr * castExpr );
+			Expression * postmutate( AddressExpr * addrExpr );
 		};
 
@@ -102,42 +103,22 @@
 		mutateAll( translationUnit, refCvt );
 		mutateAll( translationUnit, fixer );
-		mutateAll( translationUnit, elim );
 		mutateAll( translationUnit, genLval );
 		mutateAll( translationUnit, collapser );
+		mutateAll( translationUnit, elim );  // last because other passes need reference types to work
 	}
 
 	namespace {
-		Type* isLvalueRet( FunctionType *function ) {
-			if ( function->get_returnVals().empty() ) return 0;
-			Type *ty = function->get_returnVals().front()->get_type();
-			return dynamic_cast< ReferenceType * >( ty ) ;
-		}
-
-		bool isIntrinsicApp( ApplicationExpr *appExpr ) {
-			if ( VariableExpr *varExpr = dynamic_cast< VariableExpr* >( appExpr->get_function() ) ) {
-				return varExpr->get_var()->get_linkage() == LinkageSpec::Intrinsic;
-			} else {
-				return false;
-			} // if
-		}
-
-		bool isDeref( Expression * expr ) {
+		// true for intrinsic function calls that return a reference
+		bool isIntrinsicReference( Expression * expr ) {
 			if ( UntypedExpr * untyped = dynamic_cast< UntypedExpr * >( expr ) ) {
-				return InitTweak::getFunctionName( untyped ) == "*?";
+				std::string fname = InitTweak::getFunctionName( untyped );
+				// known intrinsic-reference prelude functions
+				return fname == "*?" || fname == "?[?]";
 			} else if ( ApplicationExpr * appExpr = dynamic_cast< ApplicationExpr * > ( expr ) ) {
 				if ( DeclarationWithType * func = InitTweak::getFunction( appExpr ) ) {
-					return func->get_linkage() == LinkageSpec::Intrinsic && InitTweak::getFunctionName( appExpr ) == "*?";
-				}
-			}
-			return false;
-		}
-
-		bool isIntrinsicReference( Expression * expr ) {
-			if ( isDeref( expr ) ) return true;
-			else if ( UntypedExpr * untyped = dynamic_cast< UntypedExpr * >( expr ) ) {
-				return InitTweak::getFunctionName( untyped ) == "?[?]";
-			} else if ( ApplicationExpr * appExpr = dynamic_cast< ApplicationExpr * > ( expr ) ) {
-				if ( DeclarationWithType * func = InitTweak::getFunction( appExpr ) ) {
-					return func->get_linkage() == LinkageSpec::Intrinsic && InitTweak::getFunctionName( appExpr ) == "?[?]";
+					// use type of return variable rather than expr result type, since it may have been changed to a pointer type
+					FunctionType * ftype = GenPoly::getFunctionType( func->get_type() );
+					Type * ret = ftype->get_returnVals().empty() ? nullptr : ftype->get_returnVals().front()->get_type();
+					return func->get_linkage() == LinkageSpec::Intrinsic && dynamic_cast<ReferenceType *>( ret );
 				}
 			}
@@ -186,4 +167,19 @@
 		}
 
+		Expression * ReferenceConversions::postmutate( AddressExpr * addrExpr ) {
+			// Inner expression may have been lvalue to reference conversion, which becomes an address expression.
+			// In this case, remove the outer address expression and return the argument.
+			// TODO: It's possible that this might catch too much and require a more sophisticated check.
+			if ( dynamic_cast<AddressExpr*>( addrExpr->get_arg() ) ) {
+				Expression * arg = addrExpr->get_arg();
+				arg->set_env( addrExpr->get_env() );
+				addrExpr->set_arg( nullptr );
+				addrExpr->set_env( nullptr );
+				delete addrExpr;
+				return arg;
+			}
+			return addrExpr;
+		}
+
 		Expression * ReferenceConversions::postmutate( CastExpr * castExpr ) {
 			// xxx - is it possible to convert directly between reference types with a different base? E.g.,
@@ -244,15 +240,25 @@
 				(void)refType;
 				// conversion from reference to rvalue
-				PRINT( std::cerr << "convert reference to rvalue -- *" << std::endl; )
-				PRINT( std::cerr << "was = " << castExpr << std::endl; )
+				PRINT(
+					std::cerr << "convert reference to rvalue -- *" << std::endl;
+					std::cerr << "was = " << castExpr << std::endl;
+				)
 				Expression * ret = castExpr->get_arg();
+				TypeSubstitution * env = castExpr->get_env();
+				castExpr->set_env( nullptr );
 				if ( ! isIntrinsicReference( ret ) ) {
 					// dereference if not already dereferenced
 					ret = mkDeref( ret );
 				}
-				ret->set_env( castExpr->get_env() );
-				castExpr->set_arg( nullptr );
-				castExpr->set_env( nullptr );
-				delete castExpr;
+				if ( ResolvExpr::typesCompatibleIgnoreQualifiers( castExpr->get_result(), castExpr->get_arg()->get_result()->stripReferences(), SymTab::Indexer() ) ) {
+					// can remove cast if types are compatible
+					castExpr->set_arg( nullptr );
+					delete castExpr;
+				} else {
+					// must keep cast if types are different
+					castExpr->set_arg( ret );
+					ret = castExpr;
+				}
+				ret->set_env( env );
 				PRINT( std::cerr << "now: " << ret << std::endl; )
 				return ret;
@@ -263,7 +269,8 @@
 		Type * ReferenceTypeElimination::postmutate( ReferenceType * refType ) {
 			Type * base = refType->get_base();
+			Type::Qualifiers qualifiers = refType->get_qualifiers();
 			refType->set_base( nullptr );
 			delete refType;
-			return new PointerType( Type::Qualifiers(), base );
+			return new PointerType( qualifiers, base );
 		}
 
