Index: src/ResolvExpr/AlternativeFinder.cc
===================================================================
--- src/ResolvExpr/AlternativeFinder.cc	(revision 848ce7101657d18db026ce010e05ae0026f97f55)
+++ src/ResolvExpr/AlternativeFinder.cc	(revision bf32bb8a6509388bf221983b0b4c4c8620ff76b9)
@@ -209,23 +209,17 @@
 	template< typename StructOrUnionType >
 	void AlternativeFinder::addAggMembers( StructOrUnionType *aggInst, Expression *expr, const Cost &newCost, const TypeEnvironment & env, Expression * member ) {
-		// member must be either a tuple expression or a name expr
-		if ( NameExpr * nameExpr = dynamic_cast< NameExpr * >( member ) ) {
-			const std::string & name = nameExpr->get_name();
-			std::list< Declaration* > members;
-			aggInst->lookup( name, members );
-			for ( std::list< Declaration* >::const_iterator i = members.begin(); i != members.end(); ++i ) {
-				if ( DeclarationWithType *dwt = dynamic_cast< DeclarationWithType* >( *i ) ) {
-					alternatives.push_back( Alternative( new MemberExpr( dwt, expr->clone() ), env, newCost ) );
-					renameTypes( alternatives.back().expr );
-				} else {
-					assert( false );
-				}
-			}
-		} else if ( TupleExpr * tupleExpr = dynamic_cast< TupleExpr * >( member ) ) {
-			assert( false );
-		} else {
-			// xxx - temporary
-			std::cerr << member << std::endl;
-			assertf( false, "reached unexpected case of addAggMembers" );
+
+		// by this point, member must be a name expr
+		NameExpr * nameExpr = safe_dynamic_cast< NameExpr * >( member );
+		const std::string & name = nameExpr->get_name();
+		std::list< Declaration* > members;
+		aggInst->lookup( name, members );
+		for ( std::list< Declaration* >::const_iterator i = members.begin(); i != members.end(); ++i ) {
+			if ( DeclarationWithType *dwt = dynamic_cast< DeclarationWithType* >( *i ) ) {
+				alternatives.push_back( Alternative( new MemberExpr( dwt, expr->clone() ), env, newCost ) );
+				renameTypes( alternatives.back().expr );
+			} else {
+				assert( false );
+			}
 		}
 	}
@@ -1059,4 +1053,21 @@
 		alternatives.push_back( Alternative( tupleAssignExpr->clone(), env, Cost::zero ) );
 	}
+
+	void AlternativeFinder::visit( UniqueExpr *unqExpr ) {
+		AlternativeFinder finder( indexer, env );
+		finder.findWithAdjustment( unqExpr->get_expr() );
+		for ( Alternative & alt : finder.alternatives ) {
+			// xxx - it's possible that this won't always do the right thing, i.e. two UniqueExprs
+			// with the same ID may resolve to different expressions. If this ever happens, it might be necessary
+			// to try to select an alternative here (i.e. error is there is not a unique min-cost expression).
+			// One other thought is to to resolve each ID once and map the IDs to resolved expressions,
+			// but this isn't as simple as it sounds since the alternative is not selected here, meaning it might
+			// require complicated tracking throughout the system.
+
+			// brand the new UniqueExprs with the same id so that they are recognized as the same expression by the expansion pass
+			alternatives.push_back( Alternative( new UniqueExpr( alt.expr->clone(), unqExpr->get_id() ), env, Cost::zero ) );
+		}
+	}
+
 } // namespace ResolvExpr
 
Index: src/ResolvExpr/AlternativeFinder.h
===================================================================
--- src/ResolvExpr/AlternativeFinder.h	(revision 848ce7101657d18db026ce010e05ae0026f97f55)
+++ src/ResolvExpr/AlternativeFinder.h	(revision bf32bb8a6509388bf221983b0b4c4c8620ff76b9)
@@ -69,4 +69,5 @@
 		virtual void visit( TupleIndexExpr *tupleExpr );
 		virtual void visit( TupleAssignExpr *tupleExpr );
+		virtual void visit( UniqueExpr *unqExpr );
 		/// Runs a new alternative finder on each element in [begin, end)
 		/// and writes each alternative finder to out.
