Index: src/Tuples/TupleExpansionNew.cpp
===================================================================
--- src/Tuples/TupleExpansionNew.cpp	(revision d0fcc82b63a2d803772a42ca583fb749e5aae65e)
+++ src/Tuples/TupleExpansionNew.cpp	(revision b507dcd3d87ebc8fb4b81b357f1f5ad1788af709)
@@ -15,4 +15,7 @@
 
 #include "Tuples.h"
+
+#include "AST/Pass.hpp"
+#include "Common/ScopedMap.h"
 
 namespace Tuples {
@@ -111,3 +114,180 @@
 	}
 } // namespace
+
+namespace {
+
+struct TupleAssignExpander {
+	ast::Expr const * postvisit( ast::TupleAssignExpr const * expr ) {
+		// Just move the env on the new top level expression.
+		return ast::mutate_field( expr->stmtExpr.get(),
+			&ast::TupleAssignExpr::env, expr->env.get() );
+	}
+};
+
+struct TupleTypeReplacer :
+		public ast::WithGuards,
+		public ast::WithVisitorRef<TupleTypeReplacer>,
+		public ast::WithDeclsToAdd<> {
+	void previsit( ast::ParseNode const * node ) {
+		GuardValue( location ) = &node->location;
+	}
+
+	void previsit( ast::CompoundStmt const * stmt ) {
+		previsit( (ast::ParseNode const *)stmt );
+		GuardScope( typeMap );
+	}
+
+	ast::Expr const * postvisit( ast::Expr const * expr ) {
+		if ( nullptr == expr->env ) {
+			return expr;
+		}
+		// TypeSubstitutions are never visited in the new Pass template,
+		// so it is done manually here, where all types have to be replaced.
+		return ast::mutate_field( expr, &ast::Expr::env,
+			expr->env->accept( *visitor ) );
+	}
+
+	ast::Type const * postvisit( ast::TupleType const * type ) {
+		assert( location );
+		unsigned tupleSize = type->size();
+		if ( !typeMap.count( tupleSize ) ) {
+			ast::StructDecl * decl = new ast::StructDecl( *location,
+				toString( "_tuple", tupleSize, "_" )
+			);
+			decl->body = true;
+
+			for ( size_t i = 0 ; i < tupleSize ; ++i ) {
+				ast::TypeDecl * typeParam = new ast::TypeDecl( *location,
+					toString( "tuple_param_", tupleSize, "_", i ),
+					ast::Storage::Classes(),
+					nullptr,
+					ast::TypeDecl::Dtype,
+					true
+					);
+				ast::ObjectDecl * member = new ast::ObjectDecl( *location,
+					toString( "field_", i ),
+					new ast::TypeInstType( typeParam )
+					);
+				decl->params.push_back( typeParam );
+				decl->members.push_back( member );
+			}
+
+			// Empty structures are not standard C. Add a dummy field to
+			// empty tuples to silence warnings when a compound literal
+			// `_tuple0_` is created.
+			if ( tupleSize == 0 ) {
+				decl->members.push_back(
+					new ast::ObjectDecl( *location,
+						"dummy",
+						new ast::BasicType( ast::BasicType::SignedInt ),
+						nullptr,
+						ast::Storage::Classes(),
+						// Does this have to be a C linkage?
+						ast::Linkage::C
+					)
+				);
+			}
+			typeMap[tupleSize] = decl;
+			declsToAddBefore.push_back( decl );
+		}
+
+		ast::StructDecl const * decl = typeMap[ tupleSize ];
+		ast::StructInstType * newType =
+			new ast::StructInstType( decl, type->qualifiers );
+		for ( auto pair : group_iterate( type->types, decl->params ) ) {
+			ast::Type const * t = std::get<0>( pair );
+			newType->params.push_back(
+				new ast::TypeExpr( *location, ast::deepCopy( t ) ) );
+		}
+		return newType;
+	}
+private:
+	ScopedMap< int, ast::StructDecl const * > typeMap;
+	CodeLocation const * location = nullptr;
+};
+
+struct TupleIndexExpander {
+	ast::Expr const * postvisit( ast::TupleIndexExpr const * expr ) {
+		CodeLocation const & location = expr->location;
+		ast::Expr const * tuple = expr->tuple.get();
+		assert( tuple );
+		unsigned int index = expr->index;
+		ast::TypeSubstitution const * env = expr->env.get();
+
+		if ( auto tupleExpr = dynamic_cast<ast::TupleExpr const *>( tuple ) ) {
+			// Optimization: If it is a definitely pure tuple expr,
+			// then it can reduce to the only relevant component.
+			if ( !maybeImpureIgnoreUnique( tupleExpr ) ) {
+				assert( index < tupleExpr->exprs.size() );
+				ast::ptr<ast::Expr> const & expr =
+					*std::next( tupleExpr->exprs.begin(), index );
+				ast::Expr * ret = ast::mutate( expr.get() );
+				ret->env = env;
+				return ret;
+			}
+		}
+
+		auto type = tuple->result.strict_as<ast::StructInstType>();
+		ast::StructDecl const * structDecl = type->base;
+		assert( index < structDecl->members.size() );
+		ast::ptr<ast::Decl> const & member =
+			*std::next( structDecl->members.begin(), index );
+		ast::MemberExpr * memberExpr = new ast::MemberExpr( location,
+			member.strict_as<ast::DeclWithType>(), tuple );
+		memberExpr->env = env;
+		return memberExpr;
+	}
+};
+
+ast::Expr const * replaceTupleExpr(
+		CodeLocation const & location,
+		ast::Type const * result,
+		std::vector<ast::ptr<ast::Expr>> const & exprs,
+		ast::TypeSubstitution const * env ) {
+	assert( result );
+	// A void result: It doesn't need to produce a value for cascading,
+	// just output a chain of comma exprs.
+	if ( result->isVoid() ) {
+		assert( !exprs.empty() );
+		std::vector<ast::ptr<ast::Expr>>::const_iterator iter = exprs.begin();
+		ast::Expr * expr = new ast::CastExpr( *iter++ );
+		for ( ; iter != exprs.end() ; ++iter ) {
+			expr = new ast::CommaExpr( location,
+				expr, new ast::CastExpr( *iter ) );
+		}
+		expr->env = env;
+		return expr;
+	// Typed tuple expression: Produce a compound literal which performs
+	// each of the expressions as a distinct part of its initializer. The
+	// produced compound literal may be used as part of another expression.
+	} else {
+		auto inits = map_range<std::vector<ast::ptr<ast::Init>>>( exprs,
+			[]( ast::Expr const * expr ) {
+				return new ast::SingleInit( expr->location, expr );
+			}
+		);
+		ast::Expr * expr = new ast::CompoundLiteralExpr( location,
+			result, new ast::ListInit( location, std::move( inits ) ) );
+		expr->env = env;
+		return expr;
+	}
+}
+
+struct TupleExprExpander {
+	ast::Expr const * postvisit( ast::TupleExpr const * expr ) {
+		return replaceTupleExpr( expr->location,
+			expr->result, expr->exprs, expr->env );
+	}
+};
+
+} // namespace
+
+void expandTuples( ast::TranslationUnit & translationUnit ) {
+	// These may not have to be seperate passes.
+	ast::Pass<TupleAssignExpander>::run( translationUnit );
+	ast::Pass<TupleTypeReplacer>::run( translationUnit );
+	ast::Pass<TupleIndexExpander>::run( translationUnit );
+	ast::Pass<TupleExprExpander>::run( translationUnit );
+}
+
 } // namespace Tuples
Index: src/Tuples/Tuples.h
===================================================================
--- src/Tuples/Tuples.h	(revision d0fcc82b63a2d803772a42ca583fb749e5aae65e)
+++ src/Tuples/Tuples.h	(revision b507dcd3d87ebc8fb4b81b357f1f5ad1788af709)
@@ -32,6 +32,6 @@
 	void handleTupleAssignment( ResolvExpr::AlternativeFinder & currentFinder, UntypedExpr * assign,
 		std::vector< ResolvExpr::AlternativeFinder >& args );
-	void handleTupleAssignment( 
-		ResolvExpr::CandidateFinder & finder, const ast::UntypedExpr * assign, 
+	void handleTupleAssignment(
+		ResolvExpr::CandidateFinder & finder, const ast::UntypedExpr * assign,
 		std::vector< ResolvExpr::CandidateFinder > & args );
 
@@ -43,4 +43,5 @@
 	/// replaces tuple-related elements, such as TupleType, TupleExpr, TupleAssignExpr, etc.
 	void expandTuples( std::list< Declaration * > & translationUnit );
+	void expandTuples( ast::TranslationUnit & translaionUnit );
 
 	/// replaces UniqueExprs with a temporary variable and one call
Index: src/main.cc
===================================================================
--- src/main.cc	(revision d0fcc82b63a2d803772a42ca583fb749e5aae65e)
+++ src/main.cc	(revision b507dcd3d87ebc8fb4b81b357f1f5ad1788af709)
@@ -443,4 +443,6 @@
 			PASS( "Convert Specializations",  GenPoly::convertSpecializations( transUnit ) );
 
+			PASS( "Expand Tuples", Tuples::expandTuples( transUnit ) );
+
 			translationUnit = convert( move( transUnit ) );
 		} else {
@@ -517,7 +519,6 @@
 			PASS( "Gen Waitfor", Concurrency::generateWaitFor( translationUnit ) );
 			PASS( "Convert Specializations",  GenPoly::convertSpecializations( translationUnit ) ); // needs to happen before tuple types are expanded
+			PASS( "Expand Tuples", Tuples::expandTuples( translationUnit ) ); // xxx - is this the right place for this?
 		}
-
-		PASS( "Expand Tuples", Tuples::expandTuples( translationUnit ) ); // xxx - is this the right place for this?
 
 		if ( tuplep ) {
