Index: src/AST/porting.md
===================================================================
--- src/AST/porting.md	(revision b4083644ffbeb12bf6f2d2bd551954abcfca5fe3)
+++ src/AST/porting.md	(revision 898ae07083cd0f415efeb25ebe9102635dffaa82)
@@ -305,4 +305,7 @@
 * switched order of `env`, `symtab` parameters for better consistency
 
+`findMinCost`
+* pulled out conversion cost promotion into separate `promoteCvtCost` function
+
 [1] https://gcc.gnu.org/onlinedocs/gcc-9.1.0/gcc/Type-Attributes.html#Type-Attributes
 
Index: src/ResolvExpr/CandidateFinder.cpp
===================================================================
--- src/ResolvExpr/CandidateFinder.cpp	(revision b4083644ffbeb12bf6f2d2bd551954abcfca5fe3)
+++ src/ResolvExpr/CandidateFinder.cpp	(revision 898ae07083cd0f415efeb25ebe9102635dffaa82)
@@ -27,4 +27,5 @@
 #include "Cost.h"
 #include "ExplodedArg.hpp"
+#include "RenameVars.h"           // for renameTyVars
 #include "Resolver.h"
 #include "ResolveTypeof.h"
@@ -557,9 +558,43 @@
 
 	/// Generate a cast expression from `arg` to `toType`
-	const ast::Expr * restructureCast( const ast::Expr * arg, const ast::Type * toType, bool isGenerated ) {
-		#warning unimplemented
-		(void)arg; (void)toType; (void)isGenerated;
-		assert(false);
-		return nullptr;
+	const ast::Expr * restructureCast( 
+		ast::ptr< ast::Expr > & arg, const ast::Type * toType, ast::GeneratedFlag isGenerated 
+	) {
+		if ( 
+			arg->result->size() > 1 
+			&& ! toType->isVoid() 
+			&& ! dynamic_cast< const ast::ReferenceType * >( toType ) 
+		) {
+			// Argument is a tuple and the target type is neither void nor a reference. Cast each 
+			// member of the tuple to its corresponding target type, producing the tuple of those 
+			// cast expressions. If there are more components of the tuple than components in the 
+			// target type, then excess components do not come out in the result expression (but 
+			// UniqueExpr ensures that the side effects will still be produced)
+			if ( Tuples::maybeImpureIgnoreUnique( arg ) ) {
+				// expressions which may contain side effects require a single unique instance of 
+				// the expression
+				arg = new ast::UniqueExpr{ arg->location, arg };
+			}
+			std::vector< ast::ptr< ast::Expr > > components;
+			for ( unsigned i = 0; i < toType->size(); ++i ) {
+				// cast each component
+				ast::ptr< ast::Expr > idx = new ast::TupleIndexExpr{ arg->location, arg, i };
+				components.emplace_back( 
+					restructureCast( idx, toType->getComponent( i ), isGenerated ) );
+			}
+			return new ast::TupleExpr{ arg->location, move( components ) };
+		} else {
+			// handle normally
+			return new ast::CastExpr{ arg->location, arg, toType, isGenerated };
+		}
+	}
+
+	/// Gets the name from an untyped member expression (must be NameExpr)
+	const std::string & getMemberName( const ast::UntypedMemberExpr * memberExpr ) {
+		if ( memberExpr->member.as< ast::ConstantExpr >() ) {
+			SemanticError( memberExpr, "Indexed access to struct fields unsupported: " );
+		}
+
+		return memberExpr->member.strict_as< ast::NameExpr >()->name;
 	}
 
@@ -764,7 +799,7 @@
 
 			if ( auto structInst = aggrExpr->result.as< ast::StructInstType >() ) {
-				addAggMembers( structInst, aggrExpr, cand, Cost::safe, "" );
+				addAggMembers( structInst, aggrExpr, *cand, Cost::safe, "" );
 			} else if ( auto unionInst = aggrExpr->result.as< ast::UnionInstType >() ) {
-				addAggMembers( unionInst, aggrExpr, cand, Cost::safe, "" );
+				addAggMembers( unionInst, aggrExpr, *cand, Cost::safe, "" );
 			}
 		}
@@ -773,14 +808,31 @@
 		void addAggMembers( 
 			const ast::ReferenceToType * aggrInst, const ast::Expr * expr, 
-			const CandidateRef & cand, const Cost & addedCost, const std::string & name 
+			const Candidate & cand, const Cost & addedCost, const std::string & name 
 		) {
 			for ( const ast::Decl * decl : aggrInst->lookup( name ) ) {
 				auto dwt = strict_dynamic_cast< const ast::DeclWithType * >( decl );
 				CandidateRef newCand = std::make_shared<Candidate>( 
-					*cand, new ast::MemberExpr{ expr->location, dwt, expr }, addedCost );
+					cand, new ast::MemberExpr{ expr->location, dwt, expr }, addedCost );
 				// add anonymous member interpretations whenever an aggregate value type is seen 
 				// as a member expression
 				addAnonConversions( newCand );
 				candidates.emplace_back( move( newCand ) );
+			}
+		}
+
+		/// Adds tuple member interpretations
+		void addTupleMembers( 
+			const ast::TupleType * tupleType, const ast::Expr * expr, const Candidate & cand, 
+			const Cost & addedCost, const ast::Expr * member 
+		) {
+			if ( auto constantExpr = dynamic_cast< const ast::ConstantExpr * >( member ) ) {
+				// get the value of the constant expression as an int, must be between 0 and the 
+				// length of the tuple to have meaning
+				long long val = constantExpr->intValue();
+				if ( val >= 0 && (unsigned long long)val < tupleType->size() ) {
+					addCandidate(
+						cand, new ast::TupleIndexExpr{ expr->location, expr, (unsigned)val }, 
+						addedCost );
+				}
 			}
 		}
@@ -1000,10 +1052,8 @@
 			}
 
-			// castExpr->result should be replaced with toType
-			// candidates => matches
-
-			#warning unimplemented
-			(void)castExpr;
-			assert(false);
+			// select first on argument cost, then conversion cost
+			CandidateList minArgCost = findMinCost( matches );
+			promoteCvtCost( minArgCost );
+			candidates = findMinCost( minArgCost );
 		}
 
@@ -1021,7 +1071,23 @@
 
 		void postvisit( const ast::UntypedMemberExpr * memberExpr ) {
-			#warning unimplemented
-			(void)memberExpr;
-			assert(false);
+			CandidateFinder aggFinder{ symtab, tenv };
+			aggFinder.find( memberExpr->aggregate, ResolvMode::withAdjustment() );
+			for ( CandidateRef & agg : aggFinder.candidates ) {
+				// it's okay for the aggregate expression to have reference type -- cast it to the 
+				// base type to treat the aggregate as the referenced value
+				Cost addedCost = Cost::zero;
+				agg->expr = referenceToRvalueConversion( agg->expr, addedCost );
+
+				// find member of the given type
+				if ( auto structInst = agg->expr->result.as< ast::StructInstType >() ) {
+					addAggMembers( 
+						structInst, agg->expr, *agg, addedCost, getMemberName( memberExpr ) );
+				} else if ( auto unionInst = agg->expr->result.as< ast::UnionInstType >() ) {
+					addAggMembers( 
+						unionInst, agg->expr, *agg, addedCost, getMemberName( memberExpr ) );
+				} else if ( auto tupleType = agg->expr->result.as< ast::TupleType >() ) {
+					addTupleMembers( tupleType, agg->expr, *agg, addedCost, memberExpr->member );
+				}
+			}
 		}
 
@@ -1030,8 +1096,30 @@
 		}
 
-		void postvisit( const ast::NameExpr * variableExpr ) {
-			#warning unimplemented
-			(void)variableExpr;
-			assert(false);
+		void postvisit( const ast::NameExpr * nameExpr ) {
+			std::vector< ast::SymbolTable::IdData > declList = symtab.lookupId( nameExpr->name );
+			PRINT( std::cerr << "nameExpr is " << nameExpr->name << std::endl; )
+			for ( auto & data : declList ) {
+				Cost cost = Cost::zero;
+				ast::Expr * newExpr = data.combine( nameExpr->location, cost );
+
+				CandidateRef newCand = std::make_shared<Candidate>(
+					newExpr, copy( tenv ), ast::OpenVarSet{}, ast::AssertionSet{}, Cost::zero, 
+					cost );
+				PRINT(
+					std::cerr << "decl is ";
+					ast::print( std::cerr, data.id );
+					std::cerr << std::endl;
+					std::cerr << "newExpr is ";
+					ast::print( std::cerr, newExpr );
+					std::cerr << std::endl;
+				)
+				newCand->expr = ast::mutate_field( 
+					newCand->expr.get(), &ast::Expr::result, 
+					renameTyVars( newCand->expr->result ) );
+				// add anonymous member interpretations whenever an aggregate value type is seen 
+				// as a name expression
+				addAnonConversions( newCand );
+				candidates.emplace_back( move( newCand ) );
+			}
 		}
 
@@ -1048,19 +1136,63 @@
 
 		void postvisit( const ast::SizeofExpr * sizeofExpr ) {
-			#warning unimplemented
-			(void)sizeofExpr;
-			assert(false);
+			if ( sizeofExpr->type ) {
+				addCandidate( 
+					new ast::SizeofExpr{ 
+						sizeofExpr->location, resolveTypeof( sizeofExpr->type, symtab ) }, 
+					tenv );
+			} else {
+				// find all candidates for the argument to sizeof
+				CandidateFinder finder{ symtab, tenv };
+				finder.find( sizeofExpr->expr );
+				// find the lowest-cost candidate, otherwise ambiguous
+				CandidateList winners = findMinCost( finder.candidates );
+				if ( winners.size() != 1 ) {
+					SemanticError( 
+						sizeofExpr->expr.get(), "Ambiguous expression in sizeof operand: " );
+				}
+				// return the lowest-cost candidate
+				CandidateRef & choice = winners.front();
+				choice->expr = referenceToRvalueConversion( choice->expr, choice->cost );
+				choice->cost = Cost::zero;
+				addCandidate( *choice, new ast::SizeofExpr{ sizeofExpr->location, choice->expr } );
+			}
 		}
 
 		void postvisit( const ast::AlignofExpr * alignofExpr ) {
-			#warning unimplemented
-			(void)alignofExpr;
-			assert(false);
+			if ( alignofExpr->type ) {
+				addCandidate( 
+					new ast::AlignofExpr{ 
+						alignofExpr->location, resolveTypeof( alignofExpr->type, symtab ) }, 
+					tenv );
+			} else {
+				// find all candidates for the argument to alignof
+				CandidateFinder finder{ symtab, tenv };
+				finder.find( alignofExpr->expr );
+				// find the lowest-cost candidate, otherwise ambiguous
+				CandidateList winners = findMinCost( finder.candidates );
+				if ( winners.size() != 1 ) {
+					SemanticError( 
+						alignofExpr->expr.get(), "Ambiguous expression in alignof operand: " );
+				}
+				// return the lowest-cost candidate
+				CandidateRef & choice = winners.front();
+				choice->expr = referenceToRvalueConversion( choice->expr, choice->cost );
+				choice->cost = Cost::zero;
+				addCandidate( 
+					*choice, new ast::AlignofExpr{ alignofExpr->location, choice->expr } );
+			}
 		}
 
 		void postvisit( const ast::UntypedOffsetofExpr * offsetofExpr ) {
-			#warning unimplemented
-			(void)offsetofExpr;
-			assert(false);
+			const ast::ReferenceToType * aggInst;
+			if (( aggInst = offsetofExpr->type.as< ast::StructInstType >() )) ;
+			else if (( aggInst = offsetofExpr->type.as< ast::UnionInstType >() )) ;
+			else return;
+
+			for ( const ast::Decl * member : aggInst->lookup( offsetofExpr->member ) ) {
+				auto dwt = strict_dynamic_cast< const ast::DeclWithType * >( member );
+				addCandidate( 
+					new ast::OffsetofExpr{ offsetofExpr->location, aggInst, dwt }, tenv );
+			}
 		}
 
@@ -1139,6 +1271,18 @@
 								common ) 
 						) {
-							#warning unimplemented
-							assert(false);
+							// generate typed expression
+							ast::ConditionalExpr * newExpr = new ast::ConditionalExpr{ 
+								conditionalExpr->location, r1->expr, r2->expr, r3->expr };
+							newExpr->result = common ? common : r2->expr->result;
+							// convert both options to result type
+							Cost cost = r1->cost + r2->cost + r3->cost;
+							newExpr->arg2 = computeExpressionConversionCost( 
+								newExpr->arg2, newExpr->result, symtab, env, cost );
+							newExpr->arg3 = computeExpressionConversionCost(
+								newExpr->arg3, newExpr->result, symtab, env, cost );
+							// output candidate
+							CandidateRef newCand = std::make_shared<Candidate>(
+								newExpr, move( env ), move( open ), move( need ), cost );
+							inferParameters( newCand, candidates );
 						}
 					}
@@ -1198,10 +1342,13 @@
 							common ) 
 					) {
+						// generate new expression
 						ast::RangeExpr * newExpr = 
 							new ast::RangeExpr{ rangeExpr->location, r1->expr, r2->expr };
 						newExpr->result = common ? common : r1->expr->result;
-						
-						#warning unimplemented
-						assert(false);
+						// add candidate
+						CandidateRef newCand = std::make_shared<Candidate>(
+							newExpr, move( env ), move( open ), move( need ), 
+							r1->cost + r2->cost );
+						inferParameters( newCand, candidates );
 					}
 				}
Index: src/ResolvExpr/RenameVars.cc
===================================================================
--- src/ResolvExpr/RenameVars.cc	(revision b4083644ffbeb12bf6f2d2bd551954abcfca5fe3)
+++ src/ResolvExpr/RenameVars.cc	(revision 898ae07083cd0f415efeb25ebe9102635dffaa82)
@@ -96,4 +96,11 @@
 		}
 	} // namespace
+
+	const ast::Type * renameTyVars( const ast::Type * t ) {
+		#warning unimplemented; make sure resetTyVarRenaming() updated when implemented
+		(void)t;
+		assert(false);
+		return t;
+	}
 } // namespace ResolvExpr
 
Index: src/ResolvExpr/RenameVars.h
===================================================================
--- src/ResolvExpr/RenameVars.h	(revision b4083644ffbeb12bf6f2d2bd551954abcfca5fe3)
+++ src/ResolvExpr/RenameVars.h	(revision 898ae07083cd0f415efeb25ebe9102635dffaa82)
@@ -23,7 +23,12 @@
 #include "SynTree/Visitor.h"  // for Visitor
 
+namespace ast {
+	class Type;
+}
+
 namespace ResolvExpr {
 	/// Provides a consistent renaming of forall type names in a hierarchy by prefixing them with a unique "level" ID
 	void renameTyVars( Type * );
+	const ast::Type * renameTyVars( const ast::Type * );
 
 	/// resets internal state of renamer to avoid overflow
