Index: src/GenPoly/Box.cc
===================================================================
--- src/GenPoly/Box.cc	(revision 2efe4b8f0141e181a04fcc0495d13a8c7a0a06b9)
+++ src/GenPoly/Box.cc	(revision 1cdfa82ab4efb64ed6273f4e8e3993bc8895a419)
@@ -184,4 +184,6 @@
 			/// change the type of generic aggregate members to char[]
 			void mutateMembers( AggregateDecl * aggrDecl );
+			/// returns the calculated sizeof expression for ty, or nullptr for use C sizeof()
+			Expression* genSizeof( Type* ty );
 
 			/// Enters a new scope for type-variables, adding the type variables from ty
@@ -383,6 +385,6 @@
 		unsigned long n_members = 0;
 		bool firstMember = true;
-		for ( std::list< Declaration* >::const_iterator member = structDecl->get_members().begin(); member != structDecl->get_members().end(); ++member ) {
-			DeclarationWithType *dwt = dynamic_cast< DeclarationWithType * >( *member );
+		for ( Declaration* member : structDecl->get_members() ) {
+			DeclarationWithType *dwt = dynamic_cast< DeclarationWithType * >( member );
 			assert( dwt );
 			Type *memberType = dwt->get_type();
@@ -1737,10 +1739,24 @@
 		}
 
+		Expression * PolyGenericCalculator::genSizeof( Type* ty ) {
+			if ( ArrayType * aty = dynamic_cast<ArrayType *>(ty) ) {
+				// generate calculated size for possibly generic array
+				Expression * sizeofBase = genSizeof( aty->get_base() );
+				if ( ! sizeofBase ) return nullptr;
+				Expression * dim = aty->get_dimension();
+				aty->set_dimension( nullptr );
+				return makeOp( "?*?", sizeofBase, dim );
+			} else if ( findGeneric( ty ) ) {
+				// generate calculated size for generic type
+				return new NameExpr( sizeofName( mangleType( ty ) ) );
+			} else return nullptr;
+		}
+
 		Expression *PolyGenericCalculator::postmutate( SizeofExpr *sizeofExpr ) {
-			Type *ty = sizeofExpr->get_isType() ? sizeofExpr->get_type() : sizeofExpr->get_expr()->get_result();
-			if ( findGeneric( ty ) ) {
-				return new NameExpr( sizeofName( mangleType( ty ) ) );
-			}
-			return sizeofExpr;
+			Type *ty = sizeofExpr->get_isType() ? 
+				sizeofExpr->get_type() : sizeofExpr->get_expr()->get_result();
+			
+			Expression * gen = genSizeof( ty );
+			return gen ? gen : sizeofExpr;
 		}
 
Index: src/GenPoly/GenPoly.cc
===================================================================
--- src/GenPoly/GenPoly.cc	(revision 2efe4b8f0141e181a04fcc0495d13a8c7a0a06b9)
+++ src/GenPoly/GenPoly.cc	(revision 1cdfa82ab4efb64ed6273f4e8e3993bc8895a419)
@@ -100,4 +100,6 @@
 		if ( dynamic_cast< TypeInstType * >( type ) ) {
 			return type;
+		} else if ( ArrayType * arrayType = dynamic_cast< ArrayType * >( type ) ) {
+			return isPolyType( arrayType->base, env );
 		} else if ( StructInstType *structType = dynamic_cast< StructInstType* >( type ) ) {
 			if ( hasPolyParams( structType->get_parameters(), env ) ) return type;
@@ -115,4 +117,6 @@
 				return type;
 			}
+		} else if ( ArrayType * arrayType = dynamic_cast< ArrayType * >( type ) ) {
+			return isPolyType( arrayType->base, tyVars, env );
 		} else if ( StructInstType *structType = dynamic_cast< StructInstType* >( type ) ) {
 			if ( hasPolyParams( structType->get_parameters(), tyVars, env ) ) return type;
Index: src/GenPoly/InstantiateGeneric.cc
===================================================================
--- src/GenPoly/InstantiateGeneric.cc	(revision 2efe4b8f0141e181a04fcc0495d13a8c7a0a06b9)
+++ src/GenPoly/InstantiateGeneric.cc	(revision 1cdfa82ab4efb64ed6273f4e8e3993bc8895a419)
@@ -484,4 +484,5 @@
 	Expression * FixDtypeStatic::fixMemberExpr( AggrInst * inst, MemberExpr * memberExpr ) {
 		// need to cast dtype-static member expressions to their actual type before that type is erased.
+		// NOTE: the casts here have the third argument (isGenerated) set to false so that these casts persist until Box, where they are needed.
 		auto & baseParams = *inst->get_baseParameters();
 		if ( isDtypeStatic( baseParams ) ) {
@@ -503,5 +504,5 @@
 					// Note: this currently creates more temporaries than is strictly necessary, since it does not check for duplicate uses of the same member expression.
 					static UniqueName tmpNamer( "_dtype_static_member_" );
-					Expression * init = new CastExpr( new AddressExpr( memberExpr ), new PointerType( Type::Qualifiers(), concType->clone() ) );
+					Expression * init = new CastExpr( new AddressExpr( memberExpr ), new PointerType( Type::Qualifiers(), concType->clone() ), false );
 					ObjectDecl * tmp = ObjectDecl::newObject( tmpNamer.newName(), new ReferenceType( Type::Qualifiers(), concType ), new SingleInit( init ) );
 					stmtsToAddBefore.push_back( new DeclStmt( tmp ) );
@@ -509,5 +510,5 @@
 				} else {
 					// can simply add a cast to actual type
-					return new CastExpr( memberExpr, concType );
+					return new CastExpr( memberExpr, concType, false );
 				}
 			}
Index: src/GenPoly/Lvalue.cc
===================================================================
--- src/GenPoly/Lvalue.cc	(revision 2efe4b8f0141e181a04fcc0495d13a8c7a0a06b9)
+++ src/GenPoly/Lvalue.cc	(revision 1cdfa82ab4efb64ed6273f4e8e3993bc8895a419)
@@ -45,4 +45,5 @@
 		Expression * mkDeref( Expression * arg ) {
 			if ( SymTab::dereferenceOperator ) {
+				// note: reference depth can be arbitrarily deep here, so peel off the outermost pointer/reference, not just pointer because they are effecitvely equivalent in this pass
 				VariableExpr * deref = new VariableExpr( SymTab::dereferenceOperator );
 				deref->result = new PointerType( Type::Qualifiers(), deref->result );
@@ -58,5 +59,5 @@
 		}
 
-		struct ReferenceConversions final {
+		struct ReferenceConversions final : public WithStmtsToAdd {
 			Expression * postmutate( CastExpr * castExpr );
 			Expression * postmutate( AddressExpr * addrExpr );
@@ -96,12 +97,17 @@
 		};
 
-		struct AddrRef final : public WithGuards {
+		struct AddrRef final : public WithGuards, public WithVisitorRef<AddrRef>, public WithShortCircuiting {
 			void premutate( AddressExpr * addrExpr );
 			Expression * postmutate( AddressExpr * addrExpr );
 			void premutate( Expression * expr );
+			void premutate( ApplicationExpr * appExpr );
+			void premutate( SingleInit * init );
+
+			void handleNonAddr( Expression * );
 
 			bool first = true;
 			bool current = false;
 			int refDepth = 0;
+			bool addCast = false;
 		};
 	} // namespace
@@ -113,5 +119,5 @@
 	}
 
-	void convertLvalue( std::list< Declaration* >& translationUnit ) {
+	void convertLvalue( std::list< Declaration* > & translationUnit ) {
 		PassVisitor<ReferenceConversions> refCvt;
 		PassVisitor<ReferenceTypeElimination> elim;
@@ -149,6 +155,6 @@
 					// 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 );
+					Type * ret = ftype->returnVals.empty() ? nullptr : ftype->returnVals.front()->get_type();
+					return func->linkage == LinkageSpec::Intrinsic && dynamic_cast<ReferenceType *>( ret );
 				}
 			}
@@ -159,13 +165,12 @@
 			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 );
+				Type * result = appExpr->result;
+				appExpr->result = result->stripReferences()->clone();
+				appExpr->result->set_lvalue( true );
 				if ( ! inIntrinsic ) {
 					// when not in an intrinsic function, add a cast to
 					// don't add cast when in an intrinsic function, since they already have the cast
 					Expression * ret = new CastExpr( appExpr, result );
-					ret->set_env( appExpr->get_env() );
-					appExpr->set_env( nullptr );
+					std::swap( ret->env, appExpr->env );
 					return ret;
 				}
@@ -185,10 +190,10 @@
 				assertf( ftype, "Function declaration does not have function type." );
 				// can be of differing lengths only when function is variadic
-				assertf( ftype->get_parameters().size() == appExpr->get_args().size() || ftype->get_isVarArgs(), "ApplicationExpr args do not match formal parameter type." );
+				assertf( ftype->parameters.size() == appExpr->args.size() || ftype->isVarArgs, "ApplicationExpr args do not match formal parameter type." );
 
 
 				unsigned int i = 0;
-				const unsigned int end = ftype->get_parameters().size();
-				for ( auto p : unsafe_group_iterate( appExpr->get_args(), ftype->get_parameters() ) ) {
+				const unsigned int end = ftype->parameters.size();
+				for ( auto p : unsafe_group_iterate( appExpr->args, ftype->parameters ) ) {
 					if (i == end) break;
 					Expression *& arg = std::get<0>( p );
@@ -196,22 +201,39 @@
 					PRINT(
 						std::cerr << "pair<0>: " << arg << std::endl;
+						std::cerr << " -- " << arg->result << std::endl;
 						std::cerr << "pair<1>: " << formal << std::endl;
 					)
 					if ( dynamic_cast<ReferenceType*>( formal ) ) {
-						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;
-								)
-								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() ).c_str() );
-							arg->set_result( new PointerType{ Type::Qualifiers(), baseType->clone() } );
+						PRINT(
+							std::cerr << "===formal is reference" << std::endl;
+						)
+						// TODO: it's likely that the second condition should be ... && ! isIntrinsicReference( arg ), but this requires investigation.
+
+						if ( function->get_linkage() != LinkageSpec::Intrinsic && isIntrinsicReference( arg ) ) {
+							// needed for definition of prelude functions, etc.
+							// if argument is dereference or array subscript, the result isn't REALLY a reference, but non-intrinsic functions expect a reference: take address
+
+							// NOTE: previously, this condition fixed
+							//   void f(int *&);
+							//   int & x = ...;
+							//   f(&x);
+							// But now this is taken care of by a reference cast added by AddrRef. Need to find a new
+							// example or remove this branch.
+
+							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 && InitTweak::getPointerBase( arg->result ) ) {
+						} else if ( function->get_linkage() == LinkageSpec::Intrinsic && arg->result->referenceDepth() != 0 ) {
+							// argument is a 'real' reference, but function expects a C lvalue: add a dereference to the reference-typed argument
+							PRINT(
+								std::cerr << "===is non-intrinsic arg in intrinsic call - adding deref to arg" << std::endl;
+							)
+							Type * baseType = InitTweak::getPointerBase( arg->result );
+							assertf( baseType, "parameter is reference, arg must be pointer or reference: %s", toString( arg->result ).c_str() );
+							arg->set_result( new PointerType( Type::Qualifiers(), baseType->clone() ) );
 							arg = mkDeref( arg );
+							// assertf( arg->result->referenceDepth() == 0, "Reference types should have been eliminated from intrinsic function calls, but weren't: %s", toCString( arg->result ) );
 						}
 					}
@@ -223,7 +245,10 @@
 
 		// idea: &&&E: get outer &, inner &
-		// at inner &, record depth D of reference type
+		// at inner &, record depth D of reference type of argument of &
 		// at outer &, add D derefs.
-		void AddrRef::premutate( Expression * ) {
+		void AddrRef::handleNonAddr( Expression * ) {
+			// non-address-of: reset status variables:
+			// * current expr is NOT the first address-of expr in an address-of chain
+			// * next seen address-of expr IS the first in the chain.
 			GuardValue( current );
 			GuardValue( first );
@@ -232,32 +257,77 @@
 		}
 
+		void AddrRef::premutate( Expression * expr ) {
+			handleNonAddr( expr );
+			GuardValue( addCast );
+			addCast = false;
+		}
+
 		void AddrRef::premutate( AddressExpr * ) {
 			GuardValue( current );
 			GuardValue( first );
-			current = first;
-			first = false;
-			if ( current ) {
+			current = first; // is this the first address-of in the chain?
+			first = false;   // from here out, no longer possible for next address-of to be first in chain
+			if ( current ) { // this is the outermost address-of in a chain
 				GuardValue( refDepth );
-				refDepth = 0;
+				refDepth = 0;  // set depth to 0 so that postmutate can find the innermost address-of easily
 			}
 		}
 
 		Expression * AddrRef::postmutate( AddressExpr * addrExpr ) {
+			PRINT( std::cerr << "addr ref at " << addrExpr << std::endl; )
 			if ( refDepth == 0 ) {
-				if ( ! isIntrinsicReference( addrExpr->get_arg() ) ) {
+				PRINT( std::cerr << "depth 0, get new depth..." << std::endl; )
+				// this is the innermost address-of in a chain, record depth D
+				if ( ! isIntrinsicReference( addrExpr->arg ) ) {
 					// try to avoid ?[?]
-					refDepth = addrExpr->get_arg()->get_result()->referenceDepth();
-				}
-			}
-			if ( current ) {
+					// xxx - is this condition still necessary? intrinsicReferences should have a cast around them at this point, so I don't think this condition ever fires.
+					refDepth = addrExpr->arg->result->referenceDepth();
+					PRINT( std::cerr << "arg not intrinsic reference, new depth is: " << refDepth << std::endl; )
+				} else {
+					assertf( false, "AddrRef : address-of should not have intrinsic reference argument: %s", toCString( addrExpr->arg ) );
+				}
+			}
+			if ( current ) { // this is the outermost address-of in a chain
+				PRINT( std::cerr << "current, depth is: " << refDepth << std::endl; )
 				Expression * ret = addrExpr;
 				while ( refDepth ) {
+					// add one dereference for each
 					ret = mkDeref( ret );
 					refDepth--;
 				}
+
+				// if addrExpr depth is 0, then the result is a pointer because the arg was depth 1 and not lvalue.
+				// This means the dereference result is not a reference, is lvalue, and one less pointer depth than
+				// the addrExpr. Thus the cast is meaningless.
+				// TODO: One thing to double check is whether it is possible for the types to differ outside of the single
+				// pointer level (i.e. can the base type of addrExpr differ from the type of addrExpr-arg?).
+				// If so then the cast might need to be added, conditional on a more sophisticated check.
+				if ( addCast && addrExpr->result->referenceDepth() != 0 ) {
+					PRINT( std::cerr << "adding cast to " << addrExpr->result << std::endl; )
+					return new CastExpr( ret, addrExpr->result->clone() );
+				}
 				return ret;
 			}
+			PRINT( std::cerr << "not current..." << std::endl; )
 			return addrExpr;
 		}
+
+		void AddrRef::premutate( ApplicationExpr * appExpr ) {
+			visit_children = false;
+			GuardValue( addCast );
+			handleNonAddr( appExpr );
+			for ( Expression *& arg : appExpr->args ) {
+				// each argument with address-of requires a cast
+				addCast = true;
+				arg = arg->acceptMutator( *visitor );
+			}
+		}
+
+		void AddrRef::premutate( SingleInit * ) {
+			GuardValue( addCast );
+			// each initialization context with address-of requires a cast
+			addCast = true;
+		}
+
 
 		Expression * ReferenceConversions::postmutate( AddressExpr * addrExpr ) {
@@ -276,122 +346,111 @@
 			// pointer casts in the right places.
 
-			// conversion to reference type
-			if ( ReferenceType * refType = dynamic_cast< ReferenceType * >( castExpr->get_result() ) ) {
-				(void)refType;
-				if ( ReferenceType * otherRef = dynamic_cast< ReferenceType * >( castExpr->get_arg()->get_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->get_arg() ) ) {
-						Expression * callExpr = castExpr->get_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
-						callExpr->set_result( refType->clone() );
-						// move environment out to new top-level
-						callExpr->set_env( castExpr->get_env() );
-						castExpr->set_arg( nullptr );
-						castExpr->set_env( nullptr );
-						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;
-						ret->result = castExpr->result;
-						ret->result->set_lvalue( true ); // ensure result is lvalue
-						castExpr->env = nullptr;
-						castExpr->arg = nullptr;
-						castExpr->result = nullptr;
-						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;
-						ret->result = castExpr->result;
-						castExpr->env = nullptr;
-						castExpr->arg = nullptr;
-						castExpr->result = nullptr;
-						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; )
+			// Note: reference depth difference is the determining factor in what code is run, rather than whether something is
+			// reference type or not, since conversion still needs to occur when both types are references that differ in depth.
+
+			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;
+				// the last & comes from the remaining reference conversion code
+				SemanticWarning( castExpr->arg->location, Warning::RvalueToReferenceConversion, toCString( castExpr->arg ) );
+
+				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++ ) { // xxx - maybe this should be diff-1? check how this works with reference type for srcType
+					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 );
+			}
+
+			// handle conversion between different depths
+			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;
-					ret->result = castExpr->result;
-					castExpr->env = nullptr;
-					castExpr->arg = nullptr;
-					castExpr->result = nullptr;
-					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;
+				ret->result = castExpr->result;
+				castExpr->env = nullptr;
+				castExpr->arg = nullptr;
+				castExpr->result = nullptr;
+				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;
-				} else {
+					// xxx - try removing one reference here? actually, looks like mkDeref already does this, so more closely look at the types generated.
+				}
+				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;
+				ret->result = castExpr->result;
+				ret->result->set_lvalue( true ); // ensure result is lvalue
+				castExpr->env = nullptr;
+				castExpr->arg = nullptr;
+				castExpr->result = nullptr;
 				return ret;
-			}
-			return castExpr;
+			} else {
+				assert( diff == 0 );
+				// conversion between references of the same depth
+				if ( ResolvExpr::typesCompatible( castExpr->result, castExpr->arg->result, SymTab::Indexer() ) && castExpr->isGenerated ) {
+					// Remove useless generated casts
+					PRINT(
+						std::cerr << "types are compatible, removing cast: " << castExpr << std::endl;
+						std::cerr << "-- " << castExpr->result << std::endl;
+						std::cerr << "-- " << castExpr->arg->result << std::endl;
+					)
+					Expression * ret = castExpr->arg;
+					castExpr->arg = nullptr;
+					std::swap( castExpr->env, ret->env );
+					return ret;
+				}
+				return castExpr;
+			}
 		}
 
 		Type * ReferenceTypeElimination::postmutate( ReferenceType * refType ) {
-			Type * base = refType->get_base();
+			Type * base = refType->base;
 			Type::Qualifiers qualifiers = refType->get_qualifiers();
-			refType->set_base( nullptr );
+			refType->base = nullptr;
 			return new PointerType( qualifiers, base );
 		}
@@ -400,17 +459,17 @@
 		Expression * GeneralizedLvalue::applyTransformation( Expression * expr, Expression * arg, Func mkExpr ) {
 			if ( CommaExpr * commaExpr = dynamic_cast< CommaExpr * >( arg ) ) {
-				Expression * arg1 = commaExpr->get_arg1()->clone();
-				Expression * arg2 = commaExpr->get_arg2()->clone();
+				Expression * arg1 = commaExpr->arg1->clone();
+				Expression * arg2 = commaExpr->arg2->clone();
 				Expression * ret = new CommaExpr( arg1, mkExpr( arg2 )->acceptMutator( *visitor ) );
-				ret->set_env( expr->get_env() );
-				expr->set_env( nullptr );
+				ret->env = expr->env;
+				expr->env = nullptr;
 				return ret;
 			} else if ( ConditionalExpr * condExpr = dynamic_cast< ConditionalExpr * >( arg ) ) {
-				Expression * arg1 = condExpr->get_arg1()->clone();
-				Expression * arg2 = condExpr->get_arg2()->clone();
-				Expression * arg3 = condExpr->get_arg3()->clone();
+				Expression * arg1 = condExpr->arg1->clone();
+				Expression * arg2 = condExpr->arg2->clone();
+				Expression * arg3 = condExpr->arg3->clone();
 				ConditionalExpr * ret = new ConditionalExpr( arg1, mkExpr( arg2 )->acceptMutator( *visitor ), mkExpr( arg3 )->acceptMutator( *visitor ) );
-				ret->set_env( expr->get_env() );
-				expr->set_env( nullptr );
+				ret->env = expr->env;
+				expr->env = nullptr;
 
 				// conditional expr type may not be either of the argument types, need to unify
@@ -420,6 +479,6 @@
 				AssertionSet needAssertions, haveAssertions;
 				OpenVarSet openVars;
-				unify( ret->get_arg2()->get_result(), ret->get_arg3()->get_result(), newEnv, needAssertions, haveAssertions, openVars, SymTab::Indexer(), commonType );
-				ret->set_result( commonType ? commonType : ret->get_arg2()->get_result()->clone() );
+				unify( ret->arg2->result, ret->arg3->result, newEnv, needAssertions, haveAssertions, openVars, SymTab::Indexer(), commonType );
+				ret->result = commonType ? commonType : ret->arg2->result->clone();
 				return ret;
 			}
@@ -428,13 +487,13 @@
 
 		Expression * GeneralizedLvalue::postmutate( MemberExpr * memExpr ) {
-			return applyTransformation( memExpr, memExpr->get_aggregate(), [=]( Expression * aggr ) { return new MemberExpr( memExpr->get_member(), aggr ); } );
+			return applyTransformation( memExpr, memExpr->aggregate, [=]( Expression * aggr ) { return new MemberExpr( memExpr->member, aggr ); } );
 		}
 
 		Expression * GeneralizedLvalue::postmutate( AddressExpr * addrExpr ) {
-			return applyTransformation( addrExpr, addrExpr->get_arg(), []( Expression * arg ) { return new AddressExpr( arg ); } );
+			return applyTransformation( addrExpr, addrExpr->arg, []( Expression * arg ) { return new AddressExpr( arg ); } );
 		}
 
 		Expression * CollapseAddrDeref::postmutate( AddressExpr * addrExpr ) {
-			Expression * arg = addrExpr->get_arg();
+			Expression * arg = addrExpr->arg;
 			if ( isIntrinsicReference( arg ) ) {
 				std::string fname = InitTweak::getFunctionName( arg );
@@ -442,7 +501,7 @@
 					Expression *& arg0 = InitTweak::getCallArg( arg, 0 );
 					Expression * ret = arg0;
-					ret->set_env( addrExpr->get_env() );
+					ret->set_env( addrExpr->env );
 					arg0 = nullptr;
-					addrExpr->set_env( nullptr );
+					addrExpr->env = nullptr;
 					return ret;
 				}
@@ -470,8 +529,8 @@
 					// }
 					if ( AddressExpr * addrExpr = dynamic_cast< AddressExpr * >( arg ) ) {
-						Expression * ret = addrExpr->get_arg();
-						ret->set_env( appExpr->get_env() );
-						addrExpr->set_arg( nullptr );
-						appExpr->set_env( nullptr );
+						Expression * ret = addrExpr->arg;
+						ret->env = appExpr->env;
+						addrExpr->arg = nullptr;
+						appExpr->env = nullptr;
 						return ret;
 					}
