Index: src/GenPoly/Lvalue.cc
===================================================================
--- src/GenPoly/Lvalue.cc	(revision 83794e1b7520ac5749fbef238dd63f1b9f4e9b36)
+++ src/GenPoly/Lvalue.cc	(revision 83a071f98d287fe0dd150376b0c6ea5ff812b18b)
@@ -28,4 +28,5 @@
 #include "SymTab/Indexer.h"
 #include "SymTab/Autogen.h"
+
 #include "ResolvExpr/Resolver.h"
 #include "ResolvExpr/typeops.h"
@@ -33,10 +34,7 @@
 #include "Common/UniqueName.h"
 #include "Common/utility.h"
+#include "Common/PassVisitor.h"
+
 #include "InitTweak/InitTweak.h"
-
-#include "Common/PassVisitor.h"
-
-// need to be careful about polymorphic references... e.g. in *? (___operator_deref__A0_1_0_0__Fd0_Pd0_intrinsic___1)
-// the variable is automatically dereferenced and this causes errors dereferencing void*.
 
 #if 0
@@ -57,5 +55,6 @@
 				ApplicationExpr * ret = new ApplicationExpr( deref, { arg } );
 				delete ret->get_result();
-				ret->set_result( new ReferenceType( Type::Qualifiers(), base->clone() ) );
+				ret->set_result( base->clone() );
+				ret->get_result()->set_lvalue( true );
 				return ret;
 			} else {
@@ -65,4 +64,6 @@
 
 		struct ReferenceConversions final {
+			void premutate( AddressExpr * addrExpr );
+
 			Expression * postmutate( CastExpr * castExpr );
 			Expression * postmutate( AddressExpr * addrExpr );
@@ -71,7 +72,10 @@
 		/// Intrinsic functions that take reference parameters don't REALLY take reference parameters -- their reference arguments must always be implicitly dereferenced.
 		struct FixIntrinsicArgs final {
-			Expression * postmutate( ApplicationExpr *appExpr );
-		};
-
+			Expression * postmutate( ApplicationExpr * appExpr );
+		};
+
+		struct FixIntrinsicResult final {
+			Expression * postmutate( ApplicationExpr * appExpr );
+		};
 
 		/// Replace reference types with pointer types
@@ -91,4 +95,14 @@
 			Expression * postmutate( AddressExpr * addressExpr );
 			Expression * postmutate( ApplicationExpr * appExpr );
+		};
+
+		struct AddrRef final : public WithGuards {
+			void premutate( AddressExpr * addrExpr );
+			Expression * postmutate( AddressExpr * addrExpr );
+			void premutate( Expression * expr );
+
+			bool first = true;
+			bool current = false;
+			int refDepth = 0;
 		};
 	} // namespace
@@ -107,8 +121,12 @@
 		PassVisitor<FixIntrinsicArgs> fixer;
 		PassVisitor<CollapseAddrDeref> collapser;
+		PassVisitor<AddrRef> addrRef;
+		PassVisitor<FixIntrinsicResult> intrinsicResults;
+		mutateAll( translationUnit, intrinsicResults );
+		mutateAll( translationUnit, addrRef );
 		mutateAll( translationUnit, refCvt );
 		mutateAll( translationUnit, fixer );
+		mutateAll( translationUnit, collapser );
 		mutateAll( translationUnit, genLval );
-		mutateAll( translationUnit, collapser );
 		mutateAll( translationUnit, elim );  // last because other passes need reference types to work
 
@@ -135,5 +153,18 @@
 		}
 
-		// xxx - might need to & every * (or every * that is an arg to non-intrinsic function??)
+		Expression * FixIntrinsicResult::postmutate( ApplicationExpr * appExpr ) {
+			if ( isIntrinsicReference( appExpr ) ) {
+				// eliminate reference types from intrinsic applications - now they return lvalues
+				Type * result = appExpr->get_result();
+				appExpr->set_result( result->stripReferences()->clone() );
+				appExpr->get_result()->set_lvalue( true );
+				Expression * ret = new CastExpr( appExpr, result );
+				ret->set_env( appExpr->get_env() );
+				appExpr->set_env( nullptr );
+				return ret;
+			}
+			return appExpr;
+		}
+
 		Expression * FixIntrinsicArgs::postmutate( ApplicationExpr * appExpr ) {
 			// intrinsic functions don't really take reference-typed parameters, so they require an implicit dereference on their arguments.
@@ -144,9 +175,4 @@
 				assertf( ftype->get_parameters().size() == appExpr->get_args().size() || ftype->get_isVarArgs(), "ApplicationExpr args do not match formal parameter type." );
 
-				if ( isIntrinsicReference( appExpr ) ) {
-					// eliminate reference types from intrinsic applications - now they return lvalues
-					appExpr->set_result( appExpr->get_result()->stripReferences() );
-					appExpr->get_result()->set_lvalue( true );
-				}
 
 				unsigned int i = 0;
@@ -161,14 +187,17 @@
 					)
 					if ( dynamic_cast<ReferenceType*>( formal ) ) {
-						if ( isIntrinsicReference( arg ) ) {
+						if ( isIntrinsicReference( arg ) ) { // do not combine conditions, because that changes the meaning of the else if
 							if ( function->get_linkage() != LinkageSpec::Intrinsic ) { // intrinsic functions that turn pointers into references
 								// if argument is dereference or array subscript, the result isn't REALLY a reference, so it's not necessary to fix the argument
-								PRINT( std::cerr << "is intrinsic arg in non-intrinsic call - adding address" << std::endl; )
+								PRINT(
+									std::cerr << "===is intrinsic arg in non-intrinsic call - adding address" << std::endl;
+								)
 								arg = new AddressExpr( arg );
 							}
 						} else if ( function->get_linkage() == LinkageSpec::Intrinsic ) {
+							// std::cerr << "===adding deref to arg" << std::endl;
 							// if the parameter is a reference, add a dereference to the reference-typed argument.
 							Type * baseType = InitTweak::getPointerBase( arg->get_result() );
-							assertf( baseType, "parameter is reference, arg must be pointer or reference: %s", toString( arg->get_result() ) );
+							assertf( baseType, "parameter is reference, arg must be pointer or reference: %s", toString( arg->get_result() ).c_str() );
 							PointerType * ptrType = new PointerType( Type::Qualifiers(), baseType->clone() );
 							delete arg->get_result();
@@ -183,16 +212,47 @@
 		}
 
+		// idea: &&&E: get outer &, inner &
+		// at inner &, record depth D of reference type
+		// at outer &, add D derefs.
+		void AddrRef::premutate( Expression * expr ) {
+			GuardValue( current );
+			GuardValue( first );
+			current = false;
+			first = true;
+		}
+
+		void AddrRef::premutate( AddressExpr * addrExpr ) {
+			GuardValue( current );
+			GuardValue( first );
+			current = first;
+			first = false;
+			if ( current ) {
+				GuardValue( refDepth );
+				refDepth = 0;
+			}
+		}
+
+		Expression * AddrRef::postmutate( AddressExpr * addrExpr ) {
+			if ( refDepth == 0 ) {
+				if ( ! isIntrinsicReference( addrExpr->get_arg() ) ) {
+					// try to avoid ?[?]
+					refDepth = addrExpr->get_arg()->get_result()->referenceDepth();
+				}
+			}
+			if ( current ) {
+				Expression * ret = addrExpr;
+				while ( refDepth ) {
+					ret = mkDeref( ret );
+					refDepth--;
+				}
+				return ret;
+			}
+			return addrExpr;
+		}
+
 		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;
 		}
@@ -219,4 +279,7 @@
 							std::cerr << callExpr << std::endl;
 						)
+						callExpr = new AddressExpr( callExpr ); // this doesn't work properly for multiple casts
+						delete callExpr->get_result();
+						callExpr->set_result( refType->clone() );
 						// move environment out to new top-level
 						callExpr->set_env( castExpr->get_env() );
@@ -226,5 +289,7 @@
 						return callExpr;
 					}
-					assertf( false, "non-intrinsic reference with cast of reference to reference not yet supported: ", toString( castExpr ) );
+					int depth1 = refType->referenceDepth();
+					int depth2 = otherRef->referenceDepth();
+					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;
@@ -241,10 +306,12 @@
 						// must keep cast if cast-to type is different from the actual type
 						castExpr->set_arg( ret );
-
 						return castExpr;
 					}
 					ret->set_env( castExpr->get_env() );
+					delete ret->get_result();
+					ret->set_result( castExpr->get_result() );
 					castExpr->set_env( nullptr );
 					castExpr->set_arg( nullptr );
+					castExpr->set_result( nullptr );
 					delete castExpr;
 					return ret;
@@ -296,18 +363,24 @@
 				Expression * arg1 = commaExpr->get_arg1()->clone();
 				Expression * arg2 = commaExpr->get_arg2()->clone();
+				Expression * ret = new CommaExpr( arg1, (new AddressExpr( arg2 ))->acceptMutator( *visitor ) );
+				ret->set_env( addrExpr->get_env() );
+				addrExpr->set_env( nullptr );
 				delete addrExpr;
-				return new CommaExpr( arg1, (new AddressExpr( arg2 ))->acceptMutator( *visitor ) );
+				return ret;
 			} else if ( ConditionalExpr * condExpr = dynamic_cast< ConditionalExpr * >( addrExpr->get_arg() ) ) {
 				Expression * arg1 = condExpr->get_arg1()->clone();
 				Expression * arg2 = condExpr->get_arg2()->clone();
 				Expression * arg3 = condExpr->get_arg3()->clone();
+				Expression * ret = new ConditionalExpr( arg1, (new AddressExpr( arg2 ))->acceptMutator( *visitor ), (new AddressExpr( arg3 ))->acceptMutator( *visitor ) );
+				ret->set_env( addrExpr->get_env() );
+				addrExpr->set_env( nullptr );
 				delete addrExpr;
-				return new ConditionalExpr( arg1, (new AddressExpr( arg2 ))->acceptMutator( *visitor ), (new AddressExpr( arg3 ))->acceptMutator( *visitor ) );
+				return ret;
 			}
 			return addrExpr;
 		}
 
-		Expression * CollapseAddrDeref::postmutate( AddressExpr * addressExpr ) {
-			Expression * arg = addressExpr->get_arg();
+		Expression * CollapseAddrDeref::postmutate( AddressExpr * addrExpr ) {
+			Expression * arg = addrExpr->get_arg();
 			if ( isIntrinsicReference( arg ) ) {
 				std::string fname = InitTweak::getFunctionName( arg );
@@ -315,12 +388,12 @@
 					Expression *& arg0 = InitTweak::getCallArg( arg, 0 );
 					Expression * ret = arg0;
-					ret->set_env( addressExpr->get_env() );
+					ret->set_env( addrExpr->get_env() );
 					arg0 = nullptr;
-					addressExpr->set_env( nullptr );
-					delete addressExpr;
+					addrExpr->set_env( nullptr );
+					delete addrExpr;
 					return ret;
 				}
 			}
-			return addressExpr;
+			return addrExpr;
 		}
 
